From 7f786ded48177c5b974fb2b3eecf0eb6a06bafcf Mon Sep 17 00:00:00 2001 From: Riley Park Date: Tue, 29 Nov 2022 23:53:42 -0800 Subject: [PATCH 01/12] feat(api): virtual components --- .../net/kyori/adventure/text/Component.java | 19 +++++++ .../adventure/text/TextComponentImpl.java | 12 +++-- .../adventure/text/VirtualComponent.java | 43 +++++++++++++++ .../text/VirtualComponentHolder.java | 42 +++++++++++++++ .../adventure/text/VirtualComponentImpl.java | 52 +++++++++++++++++++ .../renderer/AbstractComponentRenderer.java | 18 ++++++- 6 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 api/src/main/java/net/kyori/adventure/text/VirtualComponent.java create mode 100644 api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java create mode 100644 api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java diff --git a/api/src/main/java/net/kyori/adventure/text/Component.java b/api/src/main/java/net/kyori/adventure/text/Component.java index e120afe07..050a65c54 100644 --- a/api/src/main/java/net/kyori/adventure/text/Component.java +++ b/api/src/main/java/net/kyori/adventure/text/Component.java @@ -1257,6 +1257,25 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex return text(String.valueOf(value), color, decorations); } + /* + * -------------------------- + * ---- VirtualComponent ---- + * -------------------------- + */ + + /** + * Creates a virtual component with a value. + * + * @param virtual the value + * @return a virtual component + * @since 4.13.0 + */ + @Contract(value = "_ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual) { + requireNonNull(virtual, "virtual"); + return VirtualComponentImpl.createVirtual(virtual); + } + /* * ------------------------------- * ---- TranslatableComponent ---- diff --git a/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java index 9cac47149..a70c12c72 100644 --- a/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java @@ -36,7 +36,7 @@ import static java.util.Objects.requireNonNull; -final class TextComponentImpl extends AbstractComponent implements TextComponent { +class TextComponentImpl extends AbstractComponent implements TextComponent { private static final boolean WARN_WHEN_LEGACY_FORMATTING_DETECTED = Boolean.TRUE.equals(AdventureProperties.TEXT_WARN_WHEN_LEGACY_FORMATTING_DETECTED.value()); @VisibleForTesting static final char SECTION_CHAR = 'ยง'; @@ -56,6 +56,10 @@ static TextComponent create(final @NotNull List childre ); } + TextComponent create0(final @NotNull List children, final @NotNull Style style, final @NotNull String content) { + return create(children, style, content); + } + private static @NotNull TextComponent createDirect(final @NotNull String content) { return new TextComponentImpl(Collections.emptyList(), Style.empty(), content); } @@ -90,17 +94,17 @@ static TextComponent create(final @NotNull List childre @Override public @NotNull TextComponent content(final @NotNull String content) { if (Objects.equals(this.content, content)) return this; - return create(this.children, this.style, content); + return this.create0(this.children, this.style, content); } @Override public @NotNull TextComponent children(final @NotNull List children) { - return create(children, this.style, this.content); + return this.create0(children, this.style, this.content); } @Override public @NotNull TextComponent style(final @NotNull Style style) { - return create(this.children, style, this.content); + return this.create0(this.children, style, this.content); } @Override diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java new file mode 100644 index 000000000..d894ba6a7 --- /dev/null +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java @@ -0,0 +1,43 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2022 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.text; + +import org.jetbrains.annotations.NotNull; + +/** + * A virtual component. + * + *

This component type is transient, and not guaranteed to survive during any sort of transformations or serialization.

+ * + * @since 4.13.0 + */ +public interface VirtualComponent extends TextComponent { + /** + * Gets the virtual value holder. + * + * @return the virtual value holder + * @since 4.13.0 + */ + @NotNull VirtualComponentHolder holder(); +} diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java new file mode 100644 index 000000000..a27ecd7f6 --- /dev/null +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java @@ -0,0 +1,42 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2022 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.text; + +import org.jetbrains.annotations.UnknownNullability; + +/** + * A holder for a value. + * + * @param the stored value type + * @since 4.13.0 + */ +public interface VirtualComponentHolder { + /** + * Gets the stored value. + * + * @return the stored value + * @since 4.13.0 + */ + @UnknownNullability V unbox(); +} diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java new file mode 100644 index 000000000..cfe4bd830 --- /dev/null +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java @@ -0,0 +1,52 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2022 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.text; + +import java.util.Collections; +import java.util.List; +import net.kyori.adventure.text.format.Style; +import org.jetbrains.annotations.NotNull; + +final class VirtualComponentImpl extends TextComponentImpl implements VirtualComponent { + static VirtualComponent createVirtual(final @NotNull VirtualComponentHolder virtual) { + return new VirtualComponentImpl(Collections.emptyList(), Style.empty(), "", virtual); + } + + private final VirtualComponentHolder virtual; + + private VirtualComponentImpl(final @NotNull List children, final @NotNull Style style, final @NotNull String content, final @NotNull VirtualComponentHolder virtual) { + super(children, style, content); + this.virtual = virtual; + } + + @Override + VirtualComponent create0(final @NotNull List children, final @NotNull Style style, final @NotNull String content) { + return new VirtualComponentImpl(ComponentLike.asComponents(children, IS_NOT_EMPTY), style, content, this.virtual); + } + + @Override + public @NotNull VirtualComponentHolder holder() { + return this.virtual; + } +} diff --git a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java index 4058e96ca..2321a218b 100644 --- a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java +++ b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java @@ -33,6 +33,7 @@ import net.kyori.adventure.text.StorageNBTComponent; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.VirtualComponent; import org.jetbrains.annotations.NotNull; /** @@ -43,7 +44,10 @@ */ public abstract class AbstractComponentRenderer implements ComponentRenderer { @Override - public @NotNull Component render(final @NotNull Component component, final @NotNull C context) { + public @NotNull Component render(@NotNull Component component, final @NotNull C context) { + if (component instanceof VirtualComponent) { + component = this.renderVirtual((VirtualComponent) component, context); + } if (component instanceof TextComponent) { return this.renderText((TextComponent) component, context); } else if (component instanceof TranslatableComponent) { @@ -129,6 +133,18 @@ public abstract class AbstractComponentRenderer implements ComponentRenderer< */ protected abstract @NotNull Component renderText(final @NotNull TextComponent component, final @NotNull C context); + /** + * Renders a virtual component. + * + * @param component the component + * @param context the context + * @return the rendered component + * @since 4.13.0 + */ + protected @NotNull Component renderVirtual(final @NotNull VirtualComponent component, final @NotNull C context) { + return Component.empty(); + } + /** * Renders a translatable component. * From 95579435d7511f095c2f2ffc647ab1589b073e15 Mon Sep 17 00:00:00 2001 From: zml Date: Mon, 13 Mar 2023 17:21:43 -0700 Subject: [PATCH 02/12] chore(api): license header years --- .../main/java/net/kyori/adventure/text/VirtualComponent.java | 2 +- .../java/net/kyori/adventure/text/VirtualComponentHolder.java | 2 +- .../java/net/kyori/adventure/text/VirtualComponentImpl.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java index d894ba6a7..ae18484ca 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2022 KyoriPowered + * Copyright (c) 2017-2023 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java index a27ecd7f6..12000b4f2 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2022 KyoriPowered + * Copyright (c) 2017-2023 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java index cfe4bd830..7540d8eec 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2022 KyoriPowered + * Copyright (c) 2017-2023 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From a78ea5ba5e085b77985f6812ce276fa93d3b12f0 Mon Sep 17 00:00:00 2001 From: zml Date: Mon, 13 Mar 2023 18:02:57 -0700 Subject: [PATCH 03/12] feat(api): Make virtual components more robust, add fallback --- .../net/kyori/adventure/text/Component.java | 42 +++++++++++++++++++ .../adventure/text/TextComponentImpl.java | 2 +- .../text/VirtualComponentHolder.java | 15 +++++++ .../adventure/text/VirtualComponentImpl.java | 31 ++++++++++++++ .../renderer/AbstractComponentRenderer.java | 2 +- 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/kyori/adventure/text/Component.java b/api/src/main/java/net/kyori/adventure/text/Component.java index 050a65c54..b33f0d040 100644 --- a/api/src/main/java/net/kyori/adventure/text/Component.java +++ b/api/src/main/java/net/kyori/adventure/text/Component.java @@ -1276,6 +1276,48 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex return VirtualComponentImpl.createVirtual(virtual); } + /** + * Creates a virtual component with a value. + * + * @param virtual the value + * @param style the style + * @return a virtual component + * @since 4.13.0 + */ + @Contract(value = "_, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual, final @NotNull Style style) { + requireNonNull(virtual, "virtual"); + return VirtualComponentImpl.createVirtual(virtual); + } + + /** + * Creates a virtual component with a value. + * + * @param virtual the value + * @param style the style elements + * @return a virtual component + * @since 4.13.0 + */ + @Contract(value = "_, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual, final @NotNull StyleBuilderApplicable... style) { + requireNonNull(virtual, "virtual"); + return VirtualComponentImpl.createVirtual(Collections.emptyList(), Style.style(style), virtual); + } + + /** + * Creates a virtual component with a value. + * + * @param virtual the value + * @param style the style elements + * @return a virtual component + * @since 4.13.0 + */ + @Contract(value = "_, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual, final @NotNull Iterable style) { + requireNonNull(virtual, "virtual"); + return VirtualComponentImpl.createVirtual(Collections.emptyList(), Style.style(style), virtual); + } + /* * ------------------------------- * ---- TranslatableComponent ---- diff --git a/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java index a70c12c72..9f801a78d 100644 --- a/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/TextComponentImpl.java @@ -133,7 +133,7 @@ public String toString() { return new BuilderImpl(this); } - static final class BuilderImpl extends AbstractComponentBuilder implements TextComponent.Builder { + static class BuilderImpl extends AbstractComponentBuilder implements TextComponent.Builder { /* * We default to an empty string to avoid needing to manually set the * content of a newly-created builder when we only want to append other diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java index 12000b4f2..5d9505ee5 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java @@ -23,6 +23,7 @@ */ package net.kyori.adventure.text; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnknownNullability; /** @@ -39,4 +40,18 @@ public interface VirtualComponentHolder { * @since 4.13.0 */ @UnknownNullability V unbox(); + + /** + * Get a fallback value for when this component has been serialized without being rendered. + * + *

By default, this will be the toString{} of {@link #unbox()}.

+ * + * @return the fallback string + * @since 4.13.0 + */ + default @NotNull String fallbackString() { + final Object unboxed = this.unbox(); + return unboxed == null ? "" : unboxed.toString(); + } + } diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java index 7540d8eec..60dae08aa 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java @@ -33,6 +33,12 @@ static VirtualComponent createVirtual(final @NotNull VirtualComponentHolder v return new VirtualComponentImpl(Collections.emptyList(), Style.empty(), "", virtual); } + static VirtualComponent createVirtual(final List children, final Style style, final @NotNull VirtualComponentHolder virtual) { + final List filteredChildren = ComponentLike.asComponents(children, IS_NOT_EMPTY); + + return new VirtualComponentImpl(filteredChildren, style, "", virtual); + } + private final VirtualComponentHolder virtual; private VirtualComponentImpl(final @NotNull List children, final @NotNull Style style, final @NotNull String content, final @NotNull VirtualComponentHolder virtual) { @@ -49,4 +55,29 @@ VirtualComponent create0(final @NotNull List children, public @NotNull VirtualComponentHolder holder() { return this.virtual; } + + @Override + public @NotNull String content() { + return this.virtual.fallbackString(); + } + + @Override + public @NotNull Builder toBuilder() { + return new BuilderImpl(this); + } + + static final class BuilderImpl extends TextComponentImpl.BuilderImpl { + private final VirtualComponentHolder holder; + + BuilderImpl(final VirtualComponent other) { + super(other); + + this.holder = other.holder(); + } + + @Override + public @NotNull TextComponent build() { + return createVirtual(this.children, this.buildStyle(), this.holder); + } + } } diff --git a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java index 2321a218b..6d008e6bb 100644 --- a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java +++ b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java @@ -142,7 +142,7 @@ public abstract class AbstractComponentRenderer implements ComponentRenderer< * @since 4.13.0 */ protected @NotNull Component renderVirtual(final @NotNull VirtualComponent component, final @NotNull C context) { - return Component.empty(); + return component; // will be processed as a TextComponent instead } /** From 14718a925b5354c5af4010fd95450f999ea4d2fc Mon Sep 17 00:00:00 2001 From: zml Date: Mon, 13 Mar 2023 22:23:12 -0700 Subject: [PATCH 04/12] fix(api): Handle virtual components in compaction --- .../adventure/text/ComponentCompaction.java | 16 ++++++++++------ .../adventure/text/ComponentCompactingTest.java | 9 +++++++++ .../text/flattener/ComponentFlattenerTest.java | 10 +++++++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/net/kyori/adventure/text/ComponentCompaction.java b/api/src/main/java/net/kyori/adventure/text/ComponentCompaction.java index 87eec0b13..2cfc51082 100644 --- a/api/src/main/java/net/kyori/adventure/text/ComponentCompaction.java +++ b/api/src/main/java/net/kyori/adventure/text/ComponentCompaction.java @@ -60,7 +60,7 @@ static Component compact(final @NotNull Component self, final @Nullable Style pa } // if there is only one child, check if self a useless empty component - if (childrenSize == 1 && optimized instanceof TextComponent) { + if (childrenSize == 1 && isText(optimized)) { final TextComponent textComponent = (TextComponent) optimized; if (textComponent.content().isEmpty()) { @@ -87,7 +87,7 @@ static Component compact(final @NotNull Component self, final @Nullable Style pa child = compact(child, childParentStyle); // ignore useless empty children (regardless of its style) - if (child.children().isEmpty() && child instanceof TextComponent) { + if (child.children().isEmpty() && isText(child)) { final TextComponent textComponent = (TextComponent) child; if (textComponent.content().isEmpty()) { @@ -99,12 +99,12 @@ static Component compact(final @NotNull Component self, final @Nullable Style pa } // try to merge children into this parent component - if (optimized instanceof TextComponent) { + if (isText(optimized)) { while (!childrenToAppend.isEmpty()) { final Component child = childrenToAppend.get(0); final Style childStyle = child.style().merge(childParentStyle, Style.Merge.Strategy.IF_ABSENT_ON_TARGET); - if (child instanceof TextComponent && Objects.equals(childStyle, childParentStyle)) { + if (isText(child) && Objects.equals(childStyle, childParentStyle)) { // merge child components into the parent if they are a text component with the same effective style // in context of their parent style optimized = joinText((TextComponent) optimized, (TextComponent) child); @@ -125,7 +125,7 @@ static Component compact(final @NotNull Component self, final @Nullable Style pa final Component child = childrenToAppend.get(i); final Component neighbor = childrenToAppend.get(i + 1); - if (child.children().isEmpty() && child instanceof TextComponent && neighbor instanceof TextComponent) { + if (child.children().isEmpty() && isText(child) && isText(neighbor)) { // calculate the children's styles in context of their parent style final Style childStyle = child.style().merge(childParentStyle, Style.Merge.Strategy.IF_ABSENT_ON_TARGET); final Style neighborStyle = neighbor.style().merge(childParentStyle, Style.Merge.Strategy.IF_ABSENT_ON_TARGET); @@ -162,7 +162,7 @@ static Component compact(final @NotNull Component self, final @Nullable Style pa * @return true if the provided component is blank, false otherwise */ private static boolean isBlank(final Component component) { - if (component instanceof TextComponent) { + if (isText(component)) { final TextComponent textComponent = (TextComponent) component; final String content = textComponent.content(); @@ -215,4 +215,8 @@ private static boolean isBlank(final Component component) { private static TextComponent joinText(final TextComponent one, final TextComponent two) { return TextComponentImpl.create(two.children(), one.style(), one.content() + two.content()); } + + private static boolean isText(final Component component) { + return component instanceof TextComponent && !(component instanceof VirtualComponent); + } } diff --git a/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java b/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java index a397e4c28..718e5233b 100644 --- a/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java +++ b/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java @@ -38,6 +38,7 @@ import static net.kyori.adventure.text.Component.empty; import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.translatable; +import static net.kyori.adventure.text.Component.virtual; import static net.kyori.adventure.text.JoinConfiguration.noSeparators; import static net.kyori.adventure.text.format.Style.style; import static net.kyori.adventure.text.format.TextColor.color; @@ -417,4 +418,12 @@ void testColorPreservedWithDecorations() { assertEquals(expectedComponent, expectedComponent.compact()); } + @Test + void testVirtualComponentsPreserved() { + final Component expectedComponent = virtual(() -> "meow :3") + .append(text("3")); + + assertEquals(expectedComponent, expectedComponent.compact()); + } + } diff --git a/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java b/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java index 9e6218ade..003f869a9 100644 --- a/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java +++ b/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java @@ -31,6 +31,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.NBTComponent; import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.VirtualComponentHolder; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; @@ -43,7 +44,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; class ComponentFlattenerTest { - static class TrackingFlattener implements FlattenerListener { int pushCount; int popCount; @@ -211,6 +211,14 @@ void testKeybind() { .assertContents(); } + @Test + void testVirtualComponent() { + this.testFlatten(ComponentFlattener.basic(), Component.virtual((VirtualComponentHolder) () -> "test123")) + .assertBalanced() + .assertPushesAndPops(1) + .assertContents("test123"); + } + @Test void testCustomHandler() { final ComponentFlattener customized = ComponentFlattener.basic() From bba45c2ea3b2e2a8ab59ae92724aa1644ee09dcd Mon Sep 17 00:00:00 2001 From: zml Date: Mon, 13 Mar 2023 22:23:29 -0700 Subject: [PATCH 05/12] feat(minimessage): use virtual components to preserve rainbow/gradient tags --- .../minimessage/MiniMessageSerializer.java | 12 +- .../internal/serializer/Emitable.java | 14 + .../standard/AbstractColorChangingTag.java | 92 ++- .../minimessage/tag/standard/GradientTag.java | 46 +- .../minimessage/tag/standard/RainbowTag.java | 21 +- .../text/minimessage/AbstractTest.java | 6 + .../tag/standard/GradientTagTest.java | 768 ++++++++++-------- .../tag/standard/RainbowTagTest.java | 405 ++++----- .../tag/standard/ResetTagTest.java | 16 +- 9 files changed, 804 insertions(+), 576 deletions(-) diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageSerializer.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageSerializer.java index 5cda9955d..4365c1d0d 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageSerializer.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageSerializer.java @@ -68,10 +68,13 @@ private MiniMessageSerializer() { private static void visit(final @NotNull Component component, final Collector emitter, final SerializableResolver resolver, final boolean lastChild) { // visit self resolver.handle(component, emitter); - emitter.flushClaims(component); + Component childSource = emitter.flushClaims(component); + if (childSource == null) { + childSource = component; + } // then children - for (final Iterator it = component.children().iterator(); it.hasNext();) { + for (final Iterator it = childSource.children().iterator(); it.hasNext();) { emitter.mark(); visit(it.next(), emitter, resolver, lastChild && !it.hasNext()); } @@ -336,9 +339,11 @@ public boolean styleClaimed(final @NotNull String claimId) { return this.claimedStyleElements.contains(claimId); } - void flushClaims(final Component component) { + @Nullable Component flushClaims(final Component component) { // return: a substitute to provide children + Component ret = null; if (this.componentClaim != null) { this.componentClaim.emit(this); + ret = this.componentClaim.substitute(); this.componentClaim = null; } else if (component instanceof TextComponent) { this.text(((TextComponent) component).content()); @@ -347,6 +352,7 @@ void flushClaims(final Component component) { throw new IllegalStateException("Unclaimed component " + component); } this.claimedStyleElements.clear(); + return ret; } } diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/serializer/Emitable.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/serializer/Emitable.java index fb4fc2990..3979ecc43 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/serializer/Emitable.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/serializer/Emitable.java @@ -23,7 +23,9 @@ */ package net.kyori.adventure.text.minimessage.internal.serializer; +import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Something that holds data representable as MiniMessage tags. @@ -39,4 +41,16 @@ public interface Emitable { * @since 4.10.0 */ void emit(final @NotNull TokenEmitter emitter); + + /** + * Provide a substitute for this component's actual children. + * + *

This allows modifying tags to output original data while still transforming the created components.

+ * + * @return a potential substitute + * @since 4.13.0 + */ + default @Nullable Component substitute() { // TODO: maybe make this be only for component claims? + return null; + } } diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java index 4e7b173dc..a45877ce0 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java @@ -25,14 +25,19 @@ import java.util.Collections; import java.util.PrimitiveIterator; +import java.util.function.Consumer; import java.util.stream.Stream; import net.kyori.adventure.internal.Internals; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.VirtualComponent; +import net.kyori.adventure.text.VirtualComponentHolder; import net.kyori.adventure.text.flattener.ComponentFlattener; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.minimessage.internal.parser.node.TagNode; import net.kyori.adventure.text.minimessage.internal.parser.node.ValueNode; +import net.kyori.adventure.text.minimessage.internal.serializer.Emitable; +import net.kyori.adventure.text.minimessage.internal.serializer.TokenEmitter; import net.kyori.adventure.text.minimessage.tag.Inserting; import net.kyori.adventure.text.minimessage.tag.Modifying; import net.kyori.adventure.text.minimessage.tree.Node; @@ -95,6 +100,11 @@ public final void postVisit() { @Override public final Component apply(final @NotNull Component current, final int depth) { + if (depth == 0) { + // capture state into a virtual component, no other logic is needed in normal MM handling + return Component.virtual(new TagInfoHolder(new ComponentData(this.preserveData(), current))); + } + if ((this.disableApplyingColorDepth != -1 && depth > this.disableApplyingColorDepth) || current.style().color() != null) { if (this.disableApplyingColorDepth == -1 || depth < this.disableApplyingColorDepth) { this.disableApplyingColorDepth = depth; @@ -102,18 +112,19 @@ public final Component apply(final @NotNull Component current, final int depth) // This component has its own color applied, which overrides ours // We still want to keep track of where we are though if this is text if (current instanceof TextComponent) { - final String content = ((TextComponent) current).content(); - final int len = content.codePointCount(0, content.length()); - for (int i = 0; i < len; i++) { - // increment our color index - this.advanceColor(); - } + this.skipColorForLengthOf(((TextComponent) current).content()); } return current.children(Collections.emptyList()); } this.disableApplyingColorDepth = -1; - if (current instanceof TextComponent && ((TextComponent) current).content().length() > 0) { + if (current instanceof VirtualComponent) { + // this component has its own information, so we can't rainbowify direct content -- we can process children tho + // basically treat as if it's a non-text component + this.skipColorForLengthOf(((VirtualComponent) current).content()); + + return current.children(Collections.emptyList()); + } else if (current instanceof TextComponent && ((TextComponent) current).content().length() > 0) { final TextComponent textComponent = (TextComponent) current; final String content = textComponent.content(); @@ -138,6 +149,14 @@ public final Component apply(final @NotNull Component current, final int depth) return Component.empty().mergeStyle(current); } + private void skipColorForLengthOf(final String content) { + final int len = content.codePointCount(0, content.length()); + for (int i = 0; i < len; i++) { + // increment our color index + this.advanceColor(); + } + } + // The lifecycle protected abstract void init(); @@ -155,6 +174,14 @@ public final Component apply(final @NotNull Component current, final int depth) */ protected abstract TextColor color(); + /** + * Return an emitable that will accurately reserialize the provided input data. + * + * @return the emitable for this tag + * @since 4.13.0 + */ + protected abstract @NotNull Consumer preserveData(); + // misc @Override @@ -170,4 +197,55 @@ public final Component apply(final @NotNull Component current, final int depth) @Override public abstract int hashCode(); + + static final class ComponentData implements Emitable { + final Consumer output; + final Component originalComp; + + ComponentData(final Consumer output, final Component originalComp) { + this.output = output; + this.originalComp = originalComp; + } + + @Override + public void emit(final @NotNull TokenEmitter emitter) { + this.output.accept(emitter); + } + + @Override + public Component substitute() { + return this.originalComp; + } + } + + static final class TagInfoHolder implements VirtualComponentHolder { + private final ComponentData data; + + TagInfoHolder(final ComponentData data) { + this.data = data; + } + + @Override + public @NotNull ComponentData unbox() { + return this.data; + } + + @Override + public @NotNull String fallbackString() { + return ""; // only holds data for reserialization, not for display + } + } + + static @Nullable Emitable claimComponent(final Component comp) { + if (!(comp instanceof VirtualComponent)) { + return null; + } + + final VirtualComponentHolder holder = ((VirtualComponent) comp).holder(); + if (!(holder instanceof TagInfoHolder)) { + return null; + } + + return ((TagInfoHolder) holder).unbox(); + } } diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTag.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTag.java index 8c3178aa7..4338256a5 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTag.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTag.java @@ -29,9 +29,13 @@ import java.util.List; import java.util.Objects; import java.util.OptionalDouble; +import java.util.function.Consumer; import java.util.stream.Stream; +import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.minimessage.Context; +import net.kyori.adventure.text.minimessage.internal.serializer.SerializableResolver; +import net.kyori.adventure.text.minimessage.internal.serializer.TokenEmitter; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; @@ -47,8 +51,10 @@ */ final class GradientTag extends AbstractColorChangingTag { private static final String GRADIENT = "gradient"; + private static final TextColor DEFAULT_WHITE = TextColor.color(0xffffff); + private static final TextColor DEFAULT_BLACK = TextColor.color(0x000000); - static final TagResolver RESOLVER = TagResolver.resolver(GRADIENT, GradientTag::create); + static final TagResolver RESOLVER = SerializableResolver.claimingComponent(GRADIENT, GradientTag::create, AbstractColorChangingTag::claimComponent); private int index = 0; @@ -57,6 +63,8 @@ final class GradientTag extends AbstractColorChangingTag { private final TextColor[] colors; private @Range(from = -1, to = 1) double phase; + private final boolean negativePhase; + static Tag create(final ArgumentQueue args, final Context ctx) { double phase = 0; final List textColors; @@ -92,15 +100,17 @@ static Tag create(final ArgumentQueue args, final Context ctx) { private GradientTag(final double phase, final List colors) { if (colors.isEmpty()) { - this.colors = new TextColor[]{TextColor.color(0xffffff), TextColor.color(0x000000)}; + this.colors = new TextColor[]{DEFAULT_WHITE, DEFAULT_BLACK}; } else { this.colors = colors.toArray(new TextColor[0]); } if (phase < 0) { + this.negativePhase = true; this.phase = 1 + phase; // [-1, 0) -> [0, 1) Collections.reverse(Arrays.asList(this.colors)); } else { + this.negativePhase = false; this.phase = phase; } } @@ -132,6 +142,38 @@ protected TextColor color() { return TextColor.lerp((float) position - lowUnclamped, this.colors[low], this.colors[high]); } + @Override + protected @NotNull Consumer preserveData() { + final TextColor[] colors; + final double phase; + + if (this.negativePhase) { + colors = Arrays.copyOf(this.colors, this.colors.length); + Collections.reverse(Arrays.asList(colors)); + phase = this.phase - 1; + } else { + colors = this.colors; + phase = this.phase; + } + + return emit -> { + emit.tag(GRADIENT); + if (colors.length != 2 || !colors[0].equals(DEFAULT_WHITE) || !colors[1].equals(DEFAULT_BLACK)) { // non-default params + for (final TextColor color : colors) { + if (color instanceof NamedTextColor) { + emit.argument(NamedTextColor.NAMES.keyOrThrow((NamedTextColor) color)); + } else { + emit.argument(color.asHexString()); + } + } + } + + if (phase != 0) { + emit.argument(Double.toString(phase)); + } + }; + } + @Override public @NotNull Stream examinableProperties() { return Stream.of( diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTag.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTag.java index 101f01f63..2b7a6950b 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTag.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTag.java @@ -24,9 +24,12 @@ package net.kyori.adventure.text.minimessage.tag.standard; import java.util.Objects; +import java.util.function.Consumer; import java.util.stream.Stream; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.minimessage.Context; +import net.kyori.adventure.text.minimessage.internal.serializer.SerializableResolver; +import net.kyori.adventure.text.minimessage.internal.serializer.TokenEmitter; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; @@ -44,7 +47,7 @@ final class RainbowTag extends AbstractColorChangingTag { private static final String REVERSE = "!"; private static final String RAINBOW = "rainbow"; - static final TagResolver RESOLVER = TagResolver.resolver(RAINBOW, RainbowTag::create); + static final TagResolver RESOLVER = SerializableResolver.claimingComponent(RAINBOW, RainbowTag::create, AbstractColorChangingTag::claimComponent); private final boolean reversed; private final int phase; @@ -105,6 +108,22 @@ protected TextColor color() { return TextColor.color(HSVLike.hsvLike(hue, 1f, 1f)); } + @Override + protected @NotNull Consumer preserveData() { + final boolean reversed = this.reversed; + final int phase = this.phase; + return emit -> { + emit.tag(RAINBOW); + if (reversed && phase != 0) { + emit.argument(REVERSE + phase); + } else if (reversed) { + emit.argument(REVERSE); + } else if (phase != 0) { + emit.argument(Integer.toString(phase)); + } + }; + } + @Override public @NotNull Stream examinableProperties() { return Stream.of(ExaminableProperty.of("phase", this.phase)); diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java index bdfa6a8a4..06451ad1c 100644 --- a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java @@ -23,6 +23,7 @@ */ package net.kyori.adventure.text.minimessage; +import java.util.Arrays; import java.util.Collections; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -75,4 +76,9 @@ public static Context dummyContext(final String originalMessage) { public static ArgumentQueue emptyArgumentQueue(final Context context) { return new ArgumentQueueImpl<>(context, Collections.emptyList()); } + + public static Component virtualOfChildren(final ComponentLike... children) { + return Component.virtual(() -> "") // not part of equality... should it be? + .children(Arrays.asList(children)); + } } diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTagTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTagTest.java index 404613f14..1887c81c3 100644 --- a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTagTest.java +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/GradientTagTest.java @@ -33,6 +33,7 @@ import static net.kyori.adventure.text.Component.empty; import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.textOfChildren; import static net.kyori.adventure.text.Component.translatable; import static net.kyori.adventure.text.event.HoverEvent.showText; import static net.kyori.adventure.text.format.NamedTextColor.BLACK; @@ -55,32 +56,32 @@ void testGradient() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", WHITE)) - .append(text("|", color(0xf4f4f4))) - .append(text("|", color(0xe9e9e9))) - .append(text("|", color(0xdedede))) - .append(text("|", color(0xd3d3d3))) - .append(text("|", color(0xc8c8c8))) - .append(text("|", color(0xbcbcbc))) - .append(text("|", color(0xb1b1b1))) - .append(text("|", color(0xa6a6a6))) - .append(text("|", color(0x9b9b9b))) - .append(text("|", color(0x909090))) - .append(text("|", color(0x858585))) - .append(text("|", color(0x7a7a7a))) - .append(text("|", color(0x6f6f6f))) - .append(text("|", color(0x646464))) - .append(text("|", color(0x595959))) - .append(text("|", color(0x4e4e4e))) - .append(text("|", color(0x434343))) - .append(text("|", color(0x373737))) - .append(text("|", color(0x2c2c2c))) - .append(text("|", color(0x212121))) - .append(text("|", color(0x161616))) - .append(text("|", color(0x0b0b0b))) - .append(text("|", BLACK)) - ).append(text("!")); + .append(virtualOfChildren(textOfChildren( + text("|", WHITE), + text("|", color(0xf4f4f4)), + text("|", color(0xe9e9e9)), + text("|", color(0xdedede)), + text("|", color(0xd3d3d3)), + text("|", color(0xc8c8c8)), + text("|", color(0xbcbcbc)), + text("|", color(0xb1b1b1)), + text("|", color(0xa6a6a6)), + text("|", color(0x9b9b9b)), + text("|", color(0x909090)), + text("|", color(0x858585)), + text("|", color(0x7a7a7a)), + text("|", color(0x6f6f6f)), + text("|", color(0x646464)), + text("|", color(0x595959)), + text("|", color(0x4e4e4e)), + text("|", color(0x434343)), + text("|", color(0x373737)), + text("|", color(0x2c2c2c)), + text("|", color(0x212121)), + text("|", color(0x161616)), + text("|", color(0x0b0b0b)), + text("|", BLACK) + ))).append(text("!")); this.assertParsedEquals(expected, input); } @@ -91,32 +92,32 @@ void testGradientWithHover() { final Component expected = empty().color(YELLOW) .append(text("Woo: ")) .append(empty().hoverEvent(showText(text("This is a test"))) - .append(empty() - .append(text("|", style(WHITE))) - .append(text("|", style(color(0xf4f4f4)))) - .append(text("|", style(color(0xe9e9e9)))) - .append(text("|", style(color(0xdedede)))) - .append(text("|", style(color(0xd3d3d3)))) - .append(text("|", style(color(0xc8c8c8)))) - .append(text("|", style(color(0xbcbcbc)))) - .append(text("|", style(color(0xb1b1b1)))) - .append(text("|", style(color(0xa6a6a6)))) - .append(text("|", style(color(0x9b9b9b)))) - .append(text("|", style(color(0x909090)))) - .append(text("|", style(color(0x858585)))) - .append(text("|", style(color(0x7a7a7a)))) - .append(text("|", style(color(0x6f6f6f)))) - .append(text("|", style(color(0x646464)))) - .append(text("|", style(color(0x595959)))) - .append(text("|", style(color(0x4e4e4e)))) - .append(text("|", style(color(0x434343)))) - .append(text("|", style(color(0x373737)))) - .append(text("|", style(color(0x2c2c2c)))) - .append(text("|", style(color(0x212121)))) - .append(text("|", style(color(0x161616)))) - .append(text("|", style(color(0x0b0b0b)))) - .append(text("|", style(BLACK))) - ).append(text("!"))); + .append(virtualOfChildren(textOfChildren( + text("|", style(WHITE)), + text("|", style(color(0xf4f4f4))), + text("|", style(color(0xe9e9e9))), + text("|", style(color(0xdedede))), + text("|", style(color(0xd3d3d3))), + text("|", style(color(0xc8c8c8))), + text("|", style(color(0xbcbcbc))), + text("|", style(color(0xb1b1b1))), + text("|", style(color(0xa6a6a6))), + text("|", style(color(0x9b9b9b))), + text("|", style(color(0x909090))), + text("|", style(color(0x858585))), + text("|", style(color(0x7a7a7a))), + text("|", style(color(0x6f6f6f))), + text("|", style(color(0x646464))), + text("|", style(color(0x595959))), + text("|", style(color(0x4e4e4e))), + text("|", style(color(0x434343))), + text("|", style(color(0x373737))), + text("|", style(color(0x2c2c2c))), + text("|", style(color(0x212121))), + text("|", style(color(0x161616))), + text("|", style(color(0x0b0b0b))), + text("|", style(BLACK)) + ))).append(text("!"))); this.assertParsedEquals(expected, input); } @@ -126,32 +127,32 @@ void testGradient2() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0x5e4fa2))) - .append(text("|", color(0x65529f))) - .append(text("|", color(0x6b559c))) - .append(text("|", color(0x725898))) - .append(text("|", color(0x795b95))) - .append(text("|", color(0x7f5e92))) - .append(text("|", color(0x86618f))) - .append(text("|", color(0x8d648c))) - .append(text("|", color(0x936789))) - .append(text("|", color(0x9a6a85))) - .append(text("|", color(0xa16d82))) - .append(text("|", color(0xa7707f))) - .append(text("|", color(0xae737c))) - .append(text("|", color(0xb47679))) - .append(text("|", color(0xbb7976))) - .append(text("|", color(0xc27c72))) - .append(text("|", color(0xc87f6f))) - .append(text("|", color(0xcf826c))) - .append(text("|", color(0xd68569))) - .append(text("|", color(0xdc8866))) - .append(text("|", color(0xe38b63))) - .append(text("|", color(0xea8e5f))) - .append(text("|", color(0xf0915c))) - .append(text("|", color(0xf79459))) - ) + .append(virtualOfChildren(textOfChildren( + text("|", color(0x5e4fa2)), + text("|", color(0x65529f)), + text("|", color(0x6b559c)), + text("|", color(0x725898)), + text("|", color(0x795b95)), + text("|", color(0x7f5e92)), + text("|", color(0x86618f)), + text("|", color(0x8d648c)), + text("|", color(0x936789)), + text("|", color(0x9a6a85)), + text("|", color(0xa16d82)), + text("|", color(0xa7707f)), + text("|", color(0xae737c)), + text("|", color(0xb47679)), + text("|", color(0xbb7976)), + text("|", color(0xc27c72)), + text("|", color(0xc87f6f)), + text("|", color(0xcf826c)), + text("|", color(0xd68569)), + text("|", color(0xdc8866)), + text("|", color(0xe38b63)), + text("|", color(0xea8e5f)), + text("|", color(0xf0915c)), + text("|", color(0xf79459)) + ))) .append(text("!")); this.assertParsedEquals(expected, input); @@ -162,32 +163,33 @@ void testGradient3() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", GREEN)) - .append(text("|", color(0x55f85c))) - .append(text("|", color(0x55f064))) - .append(text("|", color(0x55e96b))) - .append(text("|", color(0x55e173))) - .append(text("|", color(0x55da7a))) - .append(text("|", color(0x55d381))) - .append(text("|", color(0x55cb89))) - .append(text("|", color(0x55c490))) - .append(text("|", color(0x55bc98))) - .append(text("|", color(0x55b59f))) - .append(text("|", color(0x55aea6))) - .append(text("|", color(0x55a6ae))) - .append(text("|", color(0x559fb5))) - .append(text("|", color(0x5598bc))) - .append(text("|", color(0x5590c4))) - .append(text("|", color(0x5589cb))) - .append(text("|", color(0x5581d3))) - .append(text("|", color(0x557ada))) - .append(text("|", color(0x5573e1))) - .append(text("|", color(0x556be9))) - .append(text("|", color(0x5564f0))) - .append(text("|", color(0x555cf8))) - .append(text("|", style(BLUE))) - ).append(text("!")); + .append(virtualOfChildren(textOfChildren( + text("|", GREEN), + text("|", color(0x55f85c)), + text("|", color(0x55f064)), + text("|", color(0x55e96b)), + text("|", color(0x55e173)), + text("|", color(0x55da7a)), + text("|", color(0x55d381)), + text("|", color(0x55cb89)), + text("|", color(0x55c490)), + text("|", color(0x55bc98)), + text("|", color(0x55b59f)), + text("|", color(0x55aea6)), + text("|", color(0x55a6ae)), + text("|", color(0x559fb5)), + text("|", color(0x5598bc)), + text("|", color(0x5590c4)), + text("|", color(0x5589cb)), + text("|", color(0x5581d3)), + text("|", color(0x557ada)), + text("|", color(0x5573e1)), + text("|", color(0x556be9)), + text("|", color(0x5564f0)), + text("|", color(0x555cf8)), + text("|", style(BLUE)) + ))) + .append(text("!")); this.assertParsedEquals(expected, input); } @@ -197,62 +199,62 @@ void testGradientMultiColor() { final String input = "Woo: ||||||||||||||||||||||||||||||||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", RED)) - .append(text("|", color(0xf25562))) - .append(text("|", color(0xe5556f))) - .append(text("|", color(0xd9557b))) - .append(text("|", color(0xcc5588))) - .append(text("|", color(0xbf5595))) - .append(text("|", color(0xb255a2))) - .append(text("|", color(0xa555af))) - .append(text("|", color(0x9855bc))) - .append(text("|", color(0x8c55c8))) - .append(text("|", color(0x7f55d5))) - .append(text("|", color(0x7255e2))) - .append(text("|", color(0x6555ef))) - .append(text("|", color(0x5855fc))) - .append(text("|", color(0x555ff5))) - .append(text("|", color(0x556be9))) - .append(text("|", color(0x5578dc))) - .append(text("|", color(0x5585cf))) - .append(text("|", color(0x5592c2))) - .append(text("|", color(0x559fb5))) - .append(text("|", color(0x55aca8))) - .append(text("|", color(0x55b89c))) - .append(text("|", color(0x55c58f))) - .append(text("|", color(0x55d282))) - .append(text("|", color(0x55df75))) - .append(text("|", color(0x55ec68))) - .append(text("|", color(0x55f95b))) - .append(text("|", color(0x5bff55))) - .append(text("|", color(0x68ff55))) - .append(text("|", color(0x75ff55))) - .append(text("|", color(0x82ff55))) - .append(text("|", color(0x8fff55))) - .append(text("|", color(0x9cff55))) - .append(text("|", color(0xa8ff55))) - .append(text("|", color(0xb5ff55))) - .append(text("|", color(0xc2ff55))) - .append(text("|", color(0xcfff55))) - .append(text("|", color(0xdcff55))) - .append(text("|", color(0xe9ff55))) - .append(text("|", color(0xf5ff55))) - .append(text("|", color(0xfffc55))) - .append(text("|", color(0xffef55))) - .append(text("|", color(0xffe255))) - .append(text("|", color(0xffd555))) - .append(text("|", color(0xffc855))) - .append(text("|", color(0xffbc55))) - .append(text("|", color(0xffaf55))) - .append(text("|", color(0xffa255))) - .append(text("|", color(0xff9555))) - .append(text("|", color(0xff8855))) - .append(text("|", color(0xff7b55))) - .append(text("|", color(0xff6f55))) - .append(text("|", color(0xff6255))) - .append(text("|", RED)) - ).append(text("!")); + .append(virtualOfChildren(textOfChildren( + text("|", RED), + text("|", color(0xf25562)), + text("|", color(0xe5556f)), + text("|", color(0xd9557b)), + text("|", color(0xcc5588)), + text("|", color(0xbf5595)), + text("|", color(0xb255a2)), + text("|", color(0xa555af)), + text("|", color(0x9855bc)), + text("|", color(0x8c55c8)), + text("|", color(0x7f55d5)), + text("|", color(0x7255e2)), + text("|", color(0x6555ef)), + text("|", color(0x5855fc)), + text("|", color(0x555ff5)), + text("|", color(0x556be9)), + text("|", color(0x5578dc)), + text("|", color(0x5585cf)), + text("|", color(0x5592c2)), + text("|", color(0x559fb5)), + text("|", color(0x55aca8)), + text("|", color(0x55b89c)), + text("|", color(0x55c58f)), + text("|", color(0x55d282)), + text("|", color(0x55df75)), + text("|", color(0x55ec68)), + text("|", color(0x55f95b)), + text("|", color(0x5bff55)), + text("|", color(0x68ff55)), + text("|", color(0x75ff55)), + text("|", color(0x82ff55)), + text("|", color(0x8fff55)), + text("|", color(0x9cff55)), + text("|", color(0xa8ff55)), + text("|", color(0xb5ff55)), + text("|", color(0xc2ff55)), + text("|", color(0xcfff55)), + text("|", color(0xdcff55)), + text("|", color(0xe9ff55)), + text("|", color(0xf5ff55)), + text("|", color(0xfffc55)), + text("|", color(0xffef55)), + text("|", color(0xffe255)), + text("|", color(0xffd555)), + text("|", color(0xffc855)), + text("|", color(0xffbc55)), + text("|", color(0xffaf55)), + text("|", color(0xffa255)), + text("|", color(0xff9555)), + text("|", color(0xff8855)), + text("|", color(0xff7b55)), + text("|", color(0xff6f55)), + text("|", color(0xff6255)), + text("|", RED) + ))).append(text("!")); this.assertParsedEquals(expected, input); } @@ -262,62 +264,62 @@ void testGradientMultiColor2() { final String input = "Woo: ||||||||||||||||||||||||||||||||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", BLACK)) - .append(text("|", color(0x0a0a0a))) - .append(text("|", color(0x131313))) - .append(text("|", color(0x1d1d1d))) - .append(text("|", color(0x262626))) - .append(text("|", color(0x303030))) - .append(text("|", color(0x3a3a3a))) - .append(text("|", color(0x434343))) - .append(text("|", color(0x4d4d4d))) - .append(text("|", color(0x575757))) - .append(text("|", color(0x606060))) - .append(text("|", color(0x6a6a6a))) - .append(text("|", color(0x737373))) - .append(text("|", color(0x7d7d7d))) - .append(text("|", color(0x878787))) - .append(text("|", color(0x909090))) - .append(text("|", color(0x9a9a9a))) - .append(text("|", color(0xa4a4a4))) - .append(text("|", color(0xadadad))) - .append(text("|", color(0xb7b7b7))) - .append(text("|", color(0xc0c0c0))) - .append(text("|", color(0xcacaca))) - .append(text("|", color(0xd4d4d4))) - .append(text("|", color(0xdddddd))) - .append(text("|", color(0xe7e7e7))) - .append(text("|", color(0xf1f1f1))) - .append(text("|", color(0xfafafa))) - .append(text("|", color(0xfafafa))) - .append(text("|", color(0xf1f1f1))) - .append(text("|", color(0xe7e7e7))) - .append(text("|", color(0xdddddd))) - .append(text("|", color(0xd4d4d4))) - .append(text("|", color(0xcacaca))) - .append(text("|", color(0xc0c0c0))) - .append(text("|", color(0xb7b7b7))) - .append(text("|", color(0xadadad))) - .append(text("|", color(0xa4a4a4))) - .append(text("|", color(0x9a9a9a))) - .append(text("|", color(0x909090))) - .append(text("|", color(0x878787))) - .append(text("|", color(0x7d7d7d))) - .append(text("|", color(0x737373))) - .append(text("|", color(0x6a6a6a))) - .append(text("|", color(0x606060))) - .append(text("|", color(0x575757))) - .append(text("|", color(0x4d4d4d))) - .append(text("|", color(0x434343))) - .append(text("|", color(0x3a3a3a))) - .append(text("|", color(0x303030))) - .append(text("|", color(0x262626))) - .append(text("|", color(0x1d1d1d))) - .append(text("|", color(0x131313))) - .append(text("|", color(0x0a0a0a))) - .append(text("|", BLACK)) - ).append(text("!")); + .append(virtualOfChildren(textOfChildren( + text("|", BLACK), + text("|", color(0x0a0a0a)), + text("|", color(0x131313)), + text("|", color(0x1d1d1d)), + text("|", color(0x262626)), + text("|", color(0x303030)), + text("|", color(0x3a3a3a)), + text("|", color(0x434343)), + text("|", color(0x4d4d4d)), + text("|", color(0x575757)), + text("|", color(0x606060)), + text("|", color(0x6a6a6a)), + text("|", color(0x737373)), + text("|", color(0x7d7d7d)), + text("|", color(0x878787)), + text("|", color(0x909090)), + text("|", color(0x9a9a9a)), + text("|", color(0xa4a4a4)), + text("|", color(0xadadad)), + text("|", color(0xb7b7b7)), + text("|", color(0xc0c0c0)), + text("|", color(0xcacaca)), + text("|", color(0xd4d4d4)), + text("|", color(0xdddddd)), + text("|", color(0xe7e7e7)), + text("|", color(0xf1f1f1)), + text("|", color(0xfafafa)), + text("|", color(0xfafafa)), + text("|", color(0xf1f1f1)), + text("|", color(0xe7e7e7)), + text("|", color(0xdddddd)), + text("|", color(0xd4d4d4)), + text("|", color(0xcacaca)), + text("|", color(0xc0c0c0)), + text("|", color(0xb7b7b7)), + text("|", color(0xadadad)), + text("|", color(0xa4a4a4)), + text("|", color(0x9a9a9a)), + text("|", color(0x909090)), + text("|", color(0x878787)), + text("|", color(0x7d7d7d)), + text("|", color(0x737373)), + text("|", color(0x6a6a6a)), + text("|", color(0x606060)), + text("|", color(0x575757)), + text("|", color(0x4d4d4d)), + text("|", color(0x434343)), + text("|", color(0x3a3a3a)), + text("|", color(0x303030)), + text("|", color(0x262626)), + text("|", color(0x1d1d1d)), + text("|", color(0x131313)), + text("|", color(0x0a0a0a)), + text("|", BLACK) + ))).append(text("!")); this.assertParsedEquals(expected, input); } @@ -327,62 +329,62 @@ void testGradientMultiColor2Phase() { final String input = "Woo: ||||||||||||||||||||||||||||||||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0xb3b3b3))) - .append(text("|", color(0xbcbcbc))) - .append(text("|", color(0xc6c6c6))) - .append(text("|", color(0xcfcfcf))) - .append(text("|", color(0xd9d9d9))) - .append(text("|", color(0xe3e3e3))) - .append(text("|", color(0xececec))) - .append(text("|", color(0xf6f6f6))) - .append(text("|", color(0xffffff))) - .append(text("|", color(0xf5f5f5))) - .append(text("|", color(0xebebeb))) - .append(text("|", color(0xe2e2e2))) - .append(text("|", color(0xd8d8d8))) - .append(text("|", color(0xcecece))) - .append(text("|", color(0xc5c5c5))) - .append(text("|", color(0xbbbbbb))) - .append(text("|", color(0xb2b2b2))) - .append(text("|", color(0xa8a8a8))) - .append(text("|", color(0x9e9e9e))) - .append(text("|", color(0x959595))) - .append(text("|", color(0x8b8b8b))) - .append(text("|", color(0x818181))) - .append(text("|", color(0x787878))) - .append(text("|", color(0x6e6e6e))) - .append(text("|", color(0x656565))) - .append(text("|", color(0x5b5b5b))) - .append(text("|", color(0x515151))) - .append(text("|", color(0x484848))) - .append(text("|", color(0x3e3e3e))) - .append(text("|", color(0x343434))) - .append(text("|", color(0x2b2b2b))) - .append(text("|", color(0x212121))) - .append(text("|", color(0x181818))) - .append(text("|", color(0x0e0e0e))) - .append(text("|", color(0x040404))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - .append(text("|", color(0x000000))) - ).append(text("!")); + .append(virtualOfChildren(textOfChildren( + text("|", color(0xb3b3b3)), + text("|", color(0xbcbcbc)), + text("|", color(0xc6c6c6)), + text("|", color(0xcfcfcf)), + text("|", color(0xd9d9d9)), + text("|", color(0xe3e3e3)), + text("|", color(0xececec)), + text("|", color(0xf6f6f6)), + text("|", color(0xffffff)), + text("|", color(0xf5f5f5)), + text("|", color(0xebebeb)), + text("|", color(0xe2e2e2)), + text("|", color(0xd8d8d8)), + text("|", color(0xcecece)), + text("|", color(0xc5c5c5)), + text("|", color(0xbbbbbb)), + text("|", color(0xb2b2b2)), + text("|", color(0xa8a8a8)), + text("|", color(0x9e9e9e)), + text("|", color(0x959595)), + text("|", color(0x8b8b8b)), + text("|", color(0x818181)), + text("|", color(0x787878)), + text("|", color(0x6e6e6e)), + text("|", color(0x656565)), + text("|", color(0x5b5b5b)), + text("|", color(0x515151)), + text("|", color(0x484848)), + text("|", color(0x3e3e3e)), + text("|", color(0x343434)), + text("|", color(0x2b2b2b)), + text("|", color(0x212121)), + text("|", color(0x181818)), + text("|", color(0x0e0e0e)), + text("|", color(0x040404)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)), + text("|", color(0x000000)) + ))).append(text("!")); this.assertParsedEquals(expected, input); } @@ -392,32 +394,32 @@ void testGradientPhase() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0x5588cc))) - .append(text("|", color(0x5581d3))) - .append(text("|", color(0x5579db))) - .append(text("|", color(0x5572e2))) - .append(text("|", color(0x556aea))) - .append(text("|", color(0x5563f1))) - .append(text("|", color(0x555cf8))) - .append(text("|", color(0x5556fe))) - .append(text("|", color(0x555df7))) - .append(text("|", color(0x5565ef))) - .append(text("|", color(0x556ce8))) - .append(text("|", color(0x5573e1))) - .append(text("|", color(0x557bd9))) - .append(text("|", color(0x5582d2))) - .append(text("|", color(0x5589cb))) - .append(text("|", color(0x5591c3))) - .append(text("|", color(0x5598bc))) - .append(text("|", color(0x55a0b4))) - .append(text("|", color(0x55a7ad))) - .append(text("|", color(0x55aea6))) - .append(text("|", color(0x55b69e))) - .append(text("|", color(0x55bd97))) - .append(text("|", color(0x55c58f))) - .append(text("|", color(0x55cc88))) - ).append(text("!")); + .append(virtualOfChildren(textOfChildren( + text("|", color(0x5588cc)), + text("|", color(0x5581d3)), + text("|", color(0x5579db)), + text("|", color(0x5572e2)), + text("|", color(0x556aea)), + text("|", color(0x5563f1)), + text("|", color(0x555cf8)), + text("|", color(0x5556fe)), + text("|", color(0x555df7)), + text("|", color(0x5565ef)), + text("|", color(0x556ce8)), + text("|", color(0x5573e1)), + text("|", color(0x557bd9)), + text("|", color(0x5582d2)), + text("|", color(0x5589cb)), + text("|", color(0x5591c3)), + text("|", color(0x5598bc)), + text("|", color(0x55a0b4)), + text("|", color(0x55a7ad)), + text("|", color(0x55aea6)), + text("|", color(0x55b69e)), + text("|", color(0x55bd97)), + text("|", color(0x55c58f)), + text("|", color(0x55cc88)) + ))).append(text("!")); this.assertParsedEquals(expected, input); } @@ -426,16 +428,20 @@ void testGradientPhase() { @Test void testGradientWithInnerTokens() { final String input = "123456!"; - final Component expected = empty() - .append(text("1", GREEN)) - .append(text("2", color(0x55dd77))) - .append(text("3", color(0x55bb99))) - .append(empty().decorate(BOLD) - .append(text("4", color(0x5599bb))) - .append(text("5", color(0x5577dd))) - .append(text("6", color(0x5555ff))) - ) - .append(text("!")); + final Component expected = textOfChildren( + virtualOfChildren( + textOfChildren( + text("1", GREEN), + text("2", color(0x55dd77)), + text("3", color(0x55bb99)) + ), + empty().decorate(BOLD) + .append(text("4", color(0x5599bb))) + .append(text("5", color(0x5577dd))) + .append(text("6", color(0x5555ff))) + ), + text("!") + ); this.assertParsedEquals(expected, input); } @@ -443,26 +449,30 @@ void testGradientWithInnerTokens() { @Test void testGradientWithInnerGradientWithInnerToken() { final String input = "123456789abc!"; - final Component expected = empty() - .append(text("1", GREEN)) - .append(text("2", color(0x55f064))) - .append(text("3", color(0x55e074))) - .append(empty() - .append(text("4", RED)) - .append(text("5", color(0xff7755))) - .append(text("6", color(0xff9955))) - .append(empty().decorate(BOLD) - .append(text("7", color(0xffbb55))) - .append(text("8", color(0xffdd55))) - .append(text("9", YELLOW)) - ) - ) - .append(empty() - .append(text("a", color(0x5574e0))) - .append(text("b", color(0x5564f0))) - .append(text("c", BLUE)) - ) - .append(text("!")); + final Component expected = textOfChildren( + virtualOfChildren( + textOfChildren( + text("1", GREEN), + text("2", color(0x55f064)), + text("3", color(0x55e074)) + ), + virtualOfChildren( + empty() + .append(text("4", RED)) + .append(text("5", color(0xff7755))) + .append(text("6", color(0xff9955))), + empty().decorate(BOLD) + .append(text("7", color(0xffbb55))) + .append(text("8", color(0xffdd55))) + .append(text("9", YELLOW)) + ), + empty() + .append(text("a", color(0x5574e0))) + .append(text("b", color(0x5564f0))) + .append(text("c", BLUE)) + ), + text("!") + ); this.assertParsedEquals(expected, input); } @@ -473,11 +483,11 @@ void testNonBmpCharactersInGradient() { final String input = "Something ๐Œฐ๐Œฑ๐Œฒ"; final Component expected = text("Something ") - .append(empty() - .append(text("๐Œฐ", BLUE)) - .append(text("๐Œฑ", color(0x55aaaa))) - .append(text("๐Œฒ", GREEN)) - ); + .append(virtualOfChildren(textOfChildren( + text("๐Œฐ", BLUE), + text("๐Œฑ", color(0x55aaaa)), + text("๐Œฒ", GREEN) + ))); this.assertParsedEquals(expected, input); } @@ -487,8 +497,8 @@ void testSingleCharGradient() { final String input1 = "A"; final String input2 = "AB"; - final Component expected1 = text("A", RED); - final Component expected2 = text().append(text("AB", RED)).build(); + final Component expected1 = virtualOfChildren(text("A", RED)); + final Component expected2 = virtualOfChildren(textOfChildren(text("AB", RED))); this.assertParsedEquals(expected1, input1); this.assertParsedEquals(expected2, input2); @@ -499,14 +509,12 @@ void testSingleCharGradient() { void testNestedGradientsDontOverrideColors() { final String input = "ab gray"; - final Component expected = Component.text() - .append( + final Component expected = virtualOfChildren( text("a", color(0x1985ff)), - text("b", color(0x00fffb)), + virtualOfChildren(text("b", color(0x00fffb))), text(" ", color(0x1f9bff)), text("gray", NamedTextColor.GRAY) - ) - .build(); + ); this.assertParsedEquals(expected, input); } @@ -516,15 +524,19 @@ void testNestedGradientsDontOverrideColors() { void testNestedGradientsReallyDontOverrideColors() { final String input = "A B C"; - final Component expected = Component.textOfChildren( - text("A", WHITE), - text(" ", color(0xd5d5ff)), - Component.textOfChildren( + final Component expected = virtualOfChildren( + textOfChildren( + text("A", NamedTextColor.WHITE), + text(" ", color(0xd5d5ff)) + ), + virtualOfChildren( + textOfChildren( text("B", YELLOW), - text(" ", color(0x80802b)), - text("C", WHITE) - ) - ); + text(" ", color(0x80802b)) + ), + text("C", WHITE) + ) + ); this.assertParsedEquals(expected, input); } @@ -534,7 +546,7 @@ void testNestedGradientsReallyDontOverrideColors() { void testDecorationsPreserved() { final Component placeholder = Component.text("b", style(TextDecoration.ITALIC.withState(true))); final String input = "acd!"; - final Component expected = Component.textOfChildren( + final Component expected = virtualOfChildren( text("a", WHITE), text("b", color(0xbfbfbf), TextDecoration.ITALIC), text("c", color(0x808080)), @@ -549,9 +561,11 @@ void testDecorationsPreserved() { @Test void testLangTagInGradient() { final String input = "ab!"; - final Component expected = Component.textOfChildren( - text("a", RED), - text("b", color(0xc6558e)), + final Component expected = virtualOfChildren( + textOfChildren( + text("a", RED), + text("b", color(0xc6558e)) + ), translatable("block.minecraft.diamond_block", color(0x8e55c6)) .append(text("!", color(0x5555ff))) ); @@ -563,7 +577,7 @@ void testLangTagInGradient() { @Test void testMultiColorPhased() { final String input = "-------"; - final Component expected = Component.textOfChildren( + final Component expected = virtualOfChildren(textOfChildren( text("-", color(0xaaaaaa)), text("-", color(0xaa7171)), text("-", color(0xaa3939)), @@ -571,7 +585,7 @@ void testMultiColorPhased() { text("-", color(0xc61c1c)), text("-", color(0xe33939)), text("-", color(0xff5555)) - ); + )); this.assertParsedEquals(expected, input); } @@ -579,15 +593,51 @@ void testMultiColorPhased() { void gh137() { final String input = ""; final String input2 = "a"; - final Component expected1 = text("a", GOLD); - final Component expected2 = text().append(text("a", GOLD), text("a", RED)).build(); - final Component expected3 = text().append(text("a", GOLD), text("a", YELLOW), text("a", RED)).build(); - final Component expected4 = text().append(text("a", GOLD), text("a", TextColor.color(0xffe339)), text("a", color(0xffc655)), text("a", RED)).build(); + final Component expected1 = virtualOfChildren(text("a", GOLD)); + final Component expected2 = virtualOfChildren(textOfChildren( + text("a", GOLD), + text("a", RED) + )); + final Component expected3 = virtualOfChildren(textOfChildren( + text("a", GOLD), + text("a", YELLOW), + text("a", RED) + )); + final Component expected4 = virtualOfChildren(textOfChildren( + text("a", GOLD), + text("a", TextColor.color(0xffe339)), + text("a", TextColor.color(0xffc655)), + text("a", RED) + )); + final Component expected5 = virtualOfChildren( + textOfChildren( + text("a", GOLD), + text("a", TextColor.color(0xffe339)), + text("a", TextColor.color(0xffc655)) + ), + text("a", RED) + ); this.assertParsedEquals(expected1, input, component("dum", text("a"))); this.assertParsedEquals(expected2, input, component("dum", text("aa"))); this.assertParsedEquals(expected3, input, component("dum", text("aaa"))); this.assertParsedEquals(expected4, input, component("dum", text("aaaa"))); - this.assertParsedEquals(expected4, input2, component("dum", text("aaa"))); + this.assertParsedEquals(expected5, input2, component("dum", text("aaa"))); + } + + @Test + void testRoundTripGradient() { + final String input = "hello world!"; + final Component parsed = PARSER.deserialize(input); + + this.assertSerializedEquals(input, parsed); + } + + @Test + void testRoundTripNestedGradient() { + final String input = "A B C"; + final Component parsed = PARSER.deserialize(input); + + this.assertSerializedEquals(input, parsed); } } diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTagTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTagTest.java index 36657bed0..3838a9dfe 100644 --- a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTagTest.java +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/RainbowTagTest.java @@ -30,6 +30,7 @@ import static net.kyori.adventure.text.Component.empty; import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.textOfChildren; import static net.kyori.adventure.text.event.ClickEvent.openUrl; import static net.kyori.adventure.text.format.NamedTextColor.BLACK; import static net.kyori.adventure.text.format.NamedTextColor.GREEN; @@ -57,32 +58,32 @@ void testRainbow() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0xff0000))) - .append(text("|", color(0xff3f00))) - .append(text("|", color(0xff7f00))) - .append(text("|", color(0xffbf00))) - .append(text("|", color(0xffff00))) - .append(text("|", color(0xbfff00))) - .append(text("|", color(0x7fff00))) - .append(text("|", color(0x3fff00))) - .append(text("|", color(0x00ff00))) - .append(text("|", color(0x00ff3f))) - .append(text("|", color(0x00ff7f))) - .append(text("|", color(0x00ffbf))) - .append(text("|", color(0x00ffff))) - .append(text("|", color(0x00bfff))) - .append(text("|", color(0x007fff))) - .append(text("|", color(0x003fff))) - .append(text("|", color(0x0000ff))) - .append(text("|", color(0x3f00ff))) - .append(text("|", color(0x7f00ff))) - .append(text("|", color(0xbf00ff))) - .append(text("|", color(0xff00ff))) - .append(text("|", color(0xff00bf))) - .append(text("|", color(0xff007f))) - .append(text("|", color(0xff003f))) - ) + .append(virtualOfChildren(textOfChildren( + text("|", color(0xff0000)), + text("|", color(0xff3f00)), + text("|", color(0xff7f00)), + text("|", color(0xffbf00)), + text("|", color(0xffff00)), + text("|", color(0xbfff00)), + text("|", color(0x7fff00)), + text("|", color(0x3fff00)), + text("|", color(0x00ff00)), + text("|", color(0x00ff3f)), + text("|", color(0x00ff7f)), + text("|", color(0x00ffbf)), + text("|", color(0x00ffff)), + text("|", color(0x00bfff)), + text("|", color(0x007fff)), + text("|", color(0x003fff)), + text("|", color(0x0000ff)), + text("|", color(0x3f00ff)), + text("|", color(0x7f00ff)), + text("|", color(0xbf00ff)), + text("|", color(0xff00ff)), + text("|", color(0xff00bf)), + text("|", color(0xff007f)), + text("|", color(0xff003f)) + ))) .append(text("!")); this.assertParsedEquals(expected, input); @@ -93,32 +94,32 @@ void testRainbowBackwards() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0xff003f))) - .append(text("|", color(0xff007f))) - .append(text("|", color(0xff00bf))) - .append(text("|", color(0xff00ff))) - .append(text("|", color(0xbf00ff))) - .append(text("|", color(0x7f00ff))) - .append(text("|", color(0x3f00ff))) - .append(text("|", color(0x0000ff))) - .append(text("|", color(0x003fff))) - .append(text("|", color(0x007fff))) - .append(text("|", color(0x00bfff))) - .append(text("|", color(0x00ffff))) - .append(text("|", color(0x00ffbf))) - .append(text("|", color(0x00ff7f))) - .append(text("|", color(0x00ff3f))) - .append(text("|", color(0x00ff00))) - .append(text("|", color(0x3fff00))) - .append(text("|", color(0x7fff00))) - .append(text("|", color(0xbfff00))) - .append(text("|", color(0xffff00))) - .append(text("|", color(0xffbf00))) - .append(text("|", color(0xff7f00))) - .append(text("|", color(0xff3f00))) - .append(text("|", color(0xff0000))) - ) + .append(virtualOfChildren(textOfChildren( + text("|", color(0xff003f)), + text("|", color(0xff007f)), + text("|", color(0xff00bf)), + text("|", color(0xff00ff)), + text("|", color(0xbf00ff)), + text("|", color(0x7f00ff)), + text("|", color(0x3f00ff)), + text("|", color(0x0000ff)), + text("|", color(0x003fff)), + text("|", color(0x007fff)), + text("|", color(0x00bfff)), + text("|", color(0x00ffff)), + text("|", color(0x00ffbf)), + text("|", color(0x00ff7f)), + text("|", color(0x00ff3f)), + text("|", color(0x00ff00)), + text("|", color(0x3fff00)), + text("|", color(0x7fff00)), + text("|", color(0xbfff00)), + text("|", color(0xffff00)), + text("|", color(0xffbf00)), + text("|", color(0xff7f00)), + text("|", color(0xff3f00)), + text("|", color(0xff0000)) + ))) .append(text("!")); this.assertParsedEquals(expected, input); @@ -129,32 +130,32 @@ void testRainbowPhase() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0xcbff00))) - .append(text("|", color(0x8cff00))) - .append(text("|", color(0x4cff00))) - .append(text("|", color(0x0cff00))) - .append(text("|", color(0x00ff33))) - .append(text("|", color(0x00ff72))) - .append(text("|", color(0x00ffb2))) - .append(text("|", color(0x00fff2))) - .append(text("|", color(0x00cbff))) - .append(text("|", color(0x008cff))) - .append(text("|", color(0x004cff))) - .append(text("|", color(0x000cff))) - .append(text("|", color(0x3200ff))) - .append(text("|", color(0x7200ff))) - .append(text("|", color(0xb200ff))) - .append(text("|", color(0xf200ff))) - .append(text("|", color(0xff00cc))) - .append(text("|", color(0xff008c))) - .append(text("|", color(0xff004c))) - .append(text("|", color(0xff000c))) - .append(text("|", color(0xff3200))) - .append(text("|", color(0xff7200))) - .append(text("|", color(0xffb200))) - .append(text("|", color(0xfff200))) - ) + .append(virtualOfChildren(textOfChildren( + text("|", color(0xcbff00)), + text("|", color(0x8cff00)), + text("|", color(0x4cff00)), + text("|", color(0x0cff00)), + text("|", color(0x00ff33)), + text("|", color(0x00ff72)), + text("|", color(0x00ffb2)), + text("|", color(0x00fff2)), + text("|", color(0x00cbff)), + text("|", color(0x008cff)), + text("|", color(0x004cff)), + text("|", color(0x000cff)), + text("|", color(0x3200ff)), + text("|", color(0x7200ff)), + text("|", color(0xb200ff)), + text("|", color(0xf200ff)), + text("|", color(0xff00cc)), + text("|", color(0xff008c)), + text("|", color(0xff004c)), + text("|", color(0xff000c)), + text("|", color(0xff3200)), + text("|", color(0xff7200)), + text("|", color(0xffb200)), + text("|", color(0xfff200)) + ))) .append(text("!")); this.assertParsedEquals(expected, input); @@ -165,32 +166,32 @@ void testRainbowPhaseBackwards() { final String input = "Woo: ||||||||||||||||||||||||!"; final Component expected = empty().color(YELLOW) .append(text("Woo: ")) - .append(empty() - .append(text("|", color(0xfff200))) - .append(text("|", color(0xffb200))) - .append(text("|", color(0xff7200))) - .append(text("|", color(0xff3200))) - .append(text("|", color(0xff000c))) - .append(text("|", color(0xff004c))) - .append(text("|", color(0xff008c))) - .append(text("|", color(0xff00cc))) - .append(text("|", color(0xf200ff))) - .append(text("|", color(0xb200ff))) - .append(text("|", color(0x7200ff))) - .append(text("|", color(0x3200ff))) - .append(text("|", color(0x000cff))) - .append(text("|", color(0x004cff))) - .append(text("|", color(0x008cff))) - .append(text("|", color(0x00cbff))) - .append(text("|", color(0x00fff2))) - .append(text("|", color(0x00ffb2))) - .append(text("|", color(0x00ff72))) - .append(text("|", color(0x00ff33))) - .append(text("|", color(0x0cff00))) - .append(text("|", color(0x4cff00))) - .append(text("|", color(0x8cff00))) - .append(text("|", color(0xcbff00))) - ) + .append(virtualOfChildren(textOfChildren( + text("|", color(0xfff200)), + text("|", color(0xffb200)), + text("|", color(0xff7200)), + text("|", color(0xff3200)), + text("|", color(0xff000c)), + text("|", color(0xff004c)), + text("|", color(0xff008c)), + text("|", color(0xff00cc)), + text("|", color(0xf200ff)), + text("|", color(0xb200ff)), + text("|", color(0x7200ff)), + text("|", color(0x3200ff)), + text("|", color(0x000cff)), + text("|", color(0x004cff)), + text("|", color(0x008cff)), + text("|", color(0x00cbff)), + text("|", color(0x00fff2)), + text("|", color(0x00ffb2)), + text("|", color(0x00ff72)), + text("|", color(0x00ff33)), + text("|", color(0x0cff00)), + text("|", color(0x4cff00)), + text("|", color(0x8cff00)), + text("|", color(0xcbff00)) + ))) .append(text("!")); this.assertParsedEquals(expected, input); @@ -202,32 +203,33 @@ void testRainbowWithInsertion() { final Component expected = empty().color(YELLOW) .append(text("Woo: ")) .append(empty().insertion("test") - .append(empty() - .append(text("|", color(0xff0000))) - .append(text("|", color(0xff3f00))) - .append(text("|", color(0xff7f00))) - .append(text("|", color(0xffbf00))) - .append(text("|", color(0xffff00))) - .append(text("|", color(0xbfff00))) - .append(text("|", color(0x7fff00))) - .append(text("|", color(0x3fff00))) - .append(text("|", color(0x00ff00))) - .append(text("|", color(0x00ff3f))) - .append(text("|", color(0x00ff7f))) - .append(text("|", color(0x00ffbf))) - .append(text("|", color(0x00ffff))) - .append(text("|", color(0x00bfff))) - .append(text("|", color(0x007fff))) - .append(text("|", color(0x003fff))) - .append(text("|", color(0x0000ff))) - .append(text("|", color(0x3f00ff))) - .append(text("|", color(0x7f00ff))) - .append(text("|", color(0xbf00ff))) - .append(text("|", color(0xff00ff))) - .append(text("|", color(0xff00bf))) - .append(text("|", color(0xff007f))) - .append(text("|", color(0xff003f))) - ).append(text("!")) + .append(virtualOfChildren(textOfChildren( + text("|", color(0xff0000)), + text("|", color(0xff3f00)), + text("|", color(0xff7f00)), + text("|", color(0xffbf00)), + text("|", color(0xffff00)), + text("|", color(0xbfff00)), + text("|", color(0x7fff00)), + text("|", color(0x3fff00)), + text("|", color(0x00ff00)), + text("|", color(0x00ff3f)), + text("|", color(0x00ff7f)), + text("|", color(0x00ffbf)), + text("|", color(0x00ffff)), + text("|", color(0x00bfff)), + text("|", color(0x007fff)), + text("|", color(0x003fff)), + text("|", color(0x0000ff)), + text("|", color(0x3f00ff)), + text("|", color(0x7f00ff)), + text("|", color(0xbf00ff)), + text("|", color(0xff00ff)), + text("|", color(0xff00bf)), + text("|", color(0xff007f)), + text("|", color(0xff003f)) + ))) + .append(text("!")) ); this.assertParsedEquals(expected, input); @@ -239,32 +241,33 @@ void testRainbowBackwardsWithInsertion() { final Component expected = empty().color(YELLOW) .append(text("Woo: ")) .append(empty().insertion("test") - .append(empty() - .append(text("|", color(0xff003f))) - .append(text("|", color(0xff007f))) - .append(text("|", color(0xff00bf))) - .append(text("|", color(0xff00ff))) - .append(text("|", color(0xbf00ff))) - .append(text("|", color(0x7f00ff))) - .append(text("|", color(0x3f00ff))) - .append(text("|", color(0x0000ff))) - .append(text("|", color(0x003fff))) - .append(text("|", color(0x007fff))) - .append(text("|", color(0x00bfff))) - .append(text("|", color(0x00ffff))) - .append(text("|", color(0x00ffbf))) - .append(text("|", color(0x00ff7f))) - .append(text("|", color(0x00ff3f))) - .append(text("|", color(0x00ff00))) - .append(text("|", color(0x3fff00))) - .append(text("|", color(0x7fff00))) - .append(text("|", color(0xbfff00))) - .append(text("|", color(0xffff00))) - .append(text("|", color(0xffbf00))) - .append(text("|", color(0xff7f00))) - .append(text("|", color(0xff3f00))) - .append(text("|", color(0xff0000))) - ).append(text("!")) + .append(virtualOfChildren(textOfChildren( + text("|", color(0xff003f)), + text("|", color(0xff007f)), + text("|", color(0xff00bf)), + text("|", color(0xff00ff)), + text("|", color(0xbf00ff)), + text("|", color(0x7f00ff)), + text("|", color(0x3f00ff)), + text("|", color(0x0000ff)), + text("|", color(0x003fff)), + text("|", color(0x007fff)), + text("|", color(0x00bfff)), + text("|", color(0x00ffff)), + text("|", color(0x00ffbf)), + text("|", color(0x00ff7f)), + text("|", color(0x00ff3f)), + text("|", color(0x00ff00)), + text("|", color(0x3fff00)), + text("|", color(0x7fff00)), + text("|", color(0xbfff00)), + text("|", color(0xffff00)), + text("|", color(0xffbf00)), + text("|", color(0xff7f00)), + text("|", color(0xff3f00)), + text("|", color(0xff0000)) + ))) + .append(text("!")) ); this.assertParsedEquals(expected, input); @@ -274,10 +277,12 @@ void testRainbowBackwardsWithInsertion() { void testRainbowWithInnerClick() { final String input = "Rainbow: GH"; final Component expected = text("Rainbow: ") - .append(empty().clickEvent(openUrl("https://github.com")) - .append(text("G").color(color(0xff0000))) - .append(text("H").color(color(0x00ffff))) - ); + .append(virtualOfChildren( + empty() + .clickEvent(openUrl("https://github.com")) + .append(text("G").color(color(0xff0000))) + .append(text("H").color(color(0x00ffff))) + )); this.assertParsedEquals(expected, input); } @@ -286,10 +291,12 @@ void testRainbowWithInnerClick() { void testRainbowBackwardsWithInnerClick() { final String input = "Rainbow: GH"; final Component expected = text("Rainbow: ") - .append(empty().clickEvent(openUrl("https://github.com")) - .append(text("G").color(color(0x00ffff))) - .append(text("H").color(color(0xff0000))) - ); + .append(virtualOfChildren( + empty() + .clickEvent(openUrl("https://github.com")) + .append(text("G").color(color(0x00ffff))) + .append(text("H").color(color(0xff0000))) + )); this.assertParsedEquals(expected, input); } @@ -297,16 +304,17 @@ void testRainbowBackwardsWithInnerClick() { // GH-125 @Test void testNoRepeatedTextAfterUnclosedRainbow() { - final Component expected = text() - .append(text('r', color(0xff0000))) - .append(text('a', color(0xff7500))) - .append(text('i', color(0xffeb00))) - .append(text('n', color(0x9cff00))) - .append(text('b', color(0x27ff00))) - .append(text('o', color(0x00ff4e))) - .append(text('w', color(0x00ffc4))) - .append(text("yellow", YELLOW)) - .build(); + final Component expected = virtualOfChildren( + text() + .append(text('r', color(0xff0000))) + .append(text('a', color(0xff7500))) + .append(text('i', color(0xffeb00))) + .append(text('n', color(0x9cff00))) + .append(text('b', color(0x27ff00))) + .append(text('o', color(0x00ff4e))) + .append(text('w', color(0x00ffc4))), + text("yellow", YELLOW) + ); final String input = "rainbowyellow"; this.assertParsedEquals(expected, input); @@ -314,33 +322,38 @@ void testNoRepeatedTextAfterUnclosedRainbow() { @Test void testRainbowOrGradientContinuesAfterColoredInner() { - final Component expectedRainbow = text() - .append(text('r', color(0xff0000))) - .append(text('a', color(0xff7f00))) - .append(text('i', color(0xffff00))) - .append(text('n', color(0x7fff00))) - .append(text("white", WHITE)) - .append(text() + final Component expectedRainbow = virtualOfChildren( + text() + .append(text('r', color(0xff0000))) + .append(text('a', color(0xff7f00))) + .append(text('i', color(0xffff00))) + .append(text('n', color(0x7fff00))), + text("white", WHITE), + text() .append(text('b', color(0x7f00ff))) .append(text('o', color(0xff00ff))) - .append(text('w', color(0xff007f)))) - .build(); + .append(text('w', color(0xff007f))) + ); final String rainbowInput = "rainwhitebow"; this.assertParsedEquals(expectedRainbow, rainbowInput); - final Component expectedGradient = text() - .append(text('g', WHITE)) - .append(text('r', color(0xeaeaea))) - .append(text('a', color(0xd5d5d5))) - .append(text('d', color(0xbfbfbf))) - .append(text("green", GREEN)) - .append(text() - .append(text('i', color(0x404040))) - .append(text('e', color(0x2b2b2b))) - .append(text('n', color(0x151515))) - .append(text('t', BLACK))) - .build(); + final Component expectedGradient = virtualOfChildren( + textOfChildren( + text('g', WHITE), + text('r', color(0xeaeaea)), + text('a', color(0xd5d5d5)), + text('d', color(0xbfbfbf)) + ), + text("green", GREEN), + textOfChildren( + text('i', color(0x404040)), + text('e', color(0x2b2b2b)), + text('n', color(0x151515)), + text('t', BLACK) + ) + ); + final String gradientInput = "gradgreenient"; this.assertParsedEquals(expectedGradient, gradientInput); @@ -349,7 +362,7 @@ void testRainbowOrGradientContinuesAfterColoredInner() { @Test void gh147() { final String input = ""; - final Component expected1 = text().append(text("y", color(0xff0000)), text("o", color(0x00ffff))).build(); + final Component expected1 = virtualOfChildren(text().append(text("y", color(0xff0000)), text("o", color(0x00ffff))).build()); this.assertParsedEquals(expected1, input, component("msg", text("yo"))); } } diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/ResetTagTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/ResetTagTest.java index 06c02038a..ca336a142 100644 --- a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/ResetTagTest.java +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/standard/ResetTagTest.java @@ -29,24 +29,24 @@ import static net.kyori.adventure.text.Component.empty; import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.textOfChildren; import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; import static net.kyori.adventure.text.format.TextColor.color; class ResetTagTest extends AbstractTest { - @Test void testReset() { final String input = "Click this wooo to insert!"; final Component expected = text("Click ") .append(empty().color(YELLOW).insertion("test") .append(text("this")) - .append(empty() - .append(text(" ", color(0xff0000))) - .append(text("w", color(0xcbff00))) - .append(text("o", color(0x00ff66))) - .append(text("o", color(0x0065ff))) - .append(text("o", color(0xcc00ff))) - ) + .append(virtualOfChildren(textOfChildren( + text(" ", color(0xff0000)), + text("w", color(0xcbff00)), + text("o", color(0x00ff66)), + text("o", color(0x0065ff)), + text("o", color(0xcc00ff)) + ))) ).append(text(" to insert!")); this.assertParsedEquals(expected, input); From 13237fb1449cb5f8ba19cb1f01dae80e3e095e62 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Sun, 2 Apr 2023 12:50:55 -0700 Subject: [PATCH 06/12] aaaaaaa --- .../net/kyori/adventure/text/Component.java | 62 +++++++++++-------- .../adventure/text/VirtualComponent.java | 18 ++++-- .../adventure/text/VirtualComponentImpl.java | 38 +++++++----- ...der.java => VirtualComponentRenderer.java} | 23 ++++--- .../renderer/AbstractComponentRenderer.java | 7 ++- .../text/ComponentCompactingTest.java | 2 +- .../flattener/ComponentFlattenerTest.java | 3 +- 7 files changed, 92 insertions(+), 61 deletions(-) rename api/src/main/java/net/kyori/adventure/text/{VirtualComponentHolder.java => VirtualComponentRenderer.java} (79%) diff --git a/api/src/main/java/net/kyori/adventure/text/Component.java b/api/src/main/java/net/kyori/adventure/text/Component.java index b33f0d040..2a5ee3584 100644 --- a/api/src/main/java/net/kyori/adventure/text/Component.java +++ b/api/src/main/java/net/kyori/adventure/text/Component.java @@ -1264,58 +1264,70 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex */ /** - * Creates a virtual component with a value. + * Creates a virtual component. * - * @param virtual the value + * @param the context type + * @param context the context type + * @param renderer the renderer * @return a virtual component - * @since 4.13.0 + * @since 4.14.0 */ - @Contract(value = "_ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual) { - requireNonNull(virtual, "virtual"); - return VirtualComponentImpl.createVirtual(virtual); + @Contract(value = "_, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer) { + requireNonNull(context, "context"); + requireNonNull(renderer, "renderer"); + return VirtualComponentImpl.createVirtual(context, renderer); } /** * Creates a virtual component with a value. * - * @param virtual the value + * @param the context type + * @param context the context type + * @param renderer the renderer * @param style the style * @return a virtual component - * @since 4.13.0 + * @since 4.14.0 */ - @Contract(value = "_, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual, final @NotNull Style style) { - requireNonNull(virtual, "virtual"); - return VirtualComponentImpl.createVirtual(virtual); + @Contract(value = "_, _, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final @NotNull Style style) { + requireNonNull(context, "context"); + requireNonNull(renderer, "renderer"); + return VirtualComponentImpl.createVirtual(context, renderer, Collections.emptyList(), style); } /** * Creates a virtual component with a value. * - * @param virtual the value + * @param the context type + * @param context the context type + * @param renderer the renderer * @param style the style elements * @return a virtual component - * @since 4.13.0 + * @since 4.14.0 */ - @Contract(value = "_, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual, final @NotNull StyleBuilderApplicable... style) { - requireNonNull(virtual, "virtual"); - return VirtualComponentImpl.createVirtual(Collections.emptyList(), Style.style(style), virtual); + @Contract(value = "_, _, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final @NotNull StyleBuilderApplicable... style) { + requireNonNull(context, "context"); + requireNonNull(renderer, "renderer"); + return VirtualComponentImpl.createVirtual(context, renderer, Collections.emptyList(), Style.style(style)); } /** * Creates a virtual component with a value. * - * @param virtual the value + * @param the context type + * @param context the context type + * @param renderer the renderer * @param style the style elements * @return a virtual component - * @since 4.13.0 + * @since 4.14.0 */ - @Contract(value = "_, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull VirtualComponentHolder virtual, final @NotNull Iterable style) { - requireNonNull(virtual, "virtual"); - return VirtualComponentImpl.createVirtual(Collections.emptyList(), Style.style(style), virtual); + @Contract(value = "_, _, _ -> new", pure = true) + static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final @NotNull Iterable style) { + requireNonNull(context, "context"); + requireNonNull(renderer, "renderer"); + return VirtualComponentImpl.createVirtual(context, renderer, Collections.emptyList(), Style.style(style)); } /* diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java index ae18484ca..d057c051f 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java @@ -30,14 +30,22 @@ * *

This component type is transient, and not guaranteed to survive during any sort of transformations or serialization.

* - * @since 4.13.0 + * @since 4.14.0 */ public interface VirtualComponent extends TextComponent { /** - * Gets the virtual value holder. + * Gets the renderer context type. * - * @return the virtual value holder - * @since 4.13.0 + * @return the renderer context type + * @since 4.14.0 */ - @NotNull VirtualComponentHolder holder(); + @NotNull Class context(); + + /** + * Gets the renderer. + * + * @return the renderer + * @since 4.14.0 + */ + @NotNull VirtualComponentRenderer renderer(); } diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java index 60dae08aa..511ee354b 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java @@ -29,36 +29,43 @@ import org.jetbrains.annotations.NotNull; final class VirtualComponentImpl extends TextComponentImpl implements VirtualComponent { - static VirtualComponent createVirtual(final @NotNull VirtualComponentHolder virtual) { - return new VirtualComponentImpl(Collections.emptyList(), Style.empty(), "", virtual); + static VirtualComponent createVirtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer) { + return createVirtual(context, renderer, Collections.emptyList(), Style.empty()); } - static VirtualComponent createVirtual(final List children, final Style style, final @NotNull VirtualComponentHolder virtual) { + static VirtualComponent createVirtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final List children, final Style style) { final List filteredChildren = ComponentLike.asComponents(children, IS_NOT_EMPTY); - return new VirtualComponentImpl(filteredChildren, style, "", virtual); + return new VirtualComponentImpl(filteredChildren, style, "", context, renderer); } - private final VirtualComponentHolder virtual; + private final Class context; + private final VirtualComponentRenderer renderer; - private VirtualComponentImpl(final @NotNull List children, final @NotNull Style style, final @NotNull String content, final @NotNull VirtualComponentHolder virtual) { + private VirtualComponentImpl(final @NotNull List children, final @NotNull Style style, final @NotNull String content, final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer) { super(children, style, content); - this.virtual = virtual; + this.context = context; + this.renderer = renderer; } @Override VirtualComponent create0(final @NotNull List children, final @NotNull Style style, final @NotNull String content) { - return new VirtualComponentImpl(ComponentLike.asComponents(children, IS_NOT_EMPTY), style, content, this.virtual); + return new VirtualComponentImpl(ComponentLike.asComponents(children, IS_NOT_EMPTY), style, content, this.context, this.renderer); } @Override - public @NotNull VirtualComponentHolder holder() { - return this.virtual; + public @NotNull Class context() { + return this.context; + } + + @Override + public @NotNull VirtualComponentRenderer renderer() { + return this.renderer; } @Override public @NotNull String content() { - return this.virtual.fallbackString(); + return this.renderer.fallbackString(); } @Override @@ -67,17 +74,18 @@ VirtualComponent create0(final @NotNull List children, } static final class BuilderImpl extends TextComponentImpl.BuilderImpl { - private final VirtualComponentHolder holder; + private final Class context; + private final VirtualComponentRenderer renderer; BuilderImpl(final VirtualComponent other) { super(other); - - this.holder = other.holder(); + this.context = other.context(); + this.renderer = other.renderer(); } @Override public @NotNull TextComponent build() { - return createVirtual(this.children, this.buildStyle(), this.holder); + return createVirtual(this.context, this.renderer, this.children, this.buildStyle()); } } } diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java similarity index 79% rename from api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java rename to api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java index 5d9505ee5..57802c5a8 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentHolder.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java @@ -29,29 +29,28 @@ /** * A holder for a value. * - * @param the stored value type - * @since 4.13.0 + * @param the context type + * @since 4.14.0 */ -public interface VirtualComponentHolder { +public interface VirtualComponentRenderer { /** - * Gets the stored value. + * Gets the value by rendering using {@code context}. * - * @return the stored value - * @since 4.13.0 + * @param context the context + * @return the rendered value + * @since 4.14.0 */ - @UnknownNullability V unbox(); + @UnknownNullability ComponentLike apply(final @NotNull C context); /** * Get a fallback value for when this component has been serialized without being rendered. * - *

By default, this will be the toString{} of {@link #unbox()}.

+ *

By default, this will be an empty string.

* * @return the fallback string - * @since 4.13.0 + * @since 4.14.0 */ default @NotNull String fallbackString() { - final Object unboxed = this.unbox(); - return unboxed == null ? "" : unboxed.toString(); + return ""; } - } diff --git a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java index 6d008e6bb..4704b9bb7 100644 --- a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java +++ b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java @@ -34,6 +34,7 @@ import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.VirtualComponent; +import net.kyori.adventure.text.VirtualComponentRenderer; import org.jetbrains.annotations.NotNull; /** @@ -139,9 +140,13 @@ public abstract class AbstractComponentRenderer implements ComponentRenderer< * @param component the component * @param context the context * @return the rendered component - * @since 4.13.0 + * @since 4.14.0 */ + @SuppressWarnings("unchecked") protected @NotNull Component renderVirtual(final @NotNull VirtualComponent component, final @NotNull C context) { + if (component.context().isInstance(context)) { + return ((VirtualComponentRenderer) component.renderer()).apply(context).asComponent(); + } return component; // will be processed as a TextComponent instead } diff --git a/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java b/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java index 718e5233b..fdf7439fd 100644 --- a/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java +++ b/api/src/test/java/net/kyori/adventure/text/ComponentCompactingTest.java @@ -420,7 +420,7 @@ void testColorPreservedWithDecorations() { @Test void testVirtualComponentsPreserved() { - final Component expectedComponent = virtual(() -> "meow :3") + final Component expectedComponent = virtual(Object.class, context -> text("meow :3")) .append(text("3")); assertEquals(expectedComponent, expectedComponent.compact()); diff --git a/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java b/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java index 003f869a9..c6437ee7c 100644 --- a/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java +++ b/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java @@ -31,7 +31,6 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.NBTComponent; import net.kyori.adventure.text.TranslatableComponent; -import net.kyori.adventure.text.VirtualComponentHolder; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; @@ -213,7 +212,7 @@ void testKeybind() { @Test void testVirtualComponent() { - this.testFlatten(ComponentFlattener.basic(), Component.virtual((VirtualComponentHolder) () -> "test123")) + this.testFlatten(ComponentFlattener.basic(), Component.virtual(Object.class, context -> Component.text("test123"))) .assertBalanced() .assertPushesAndPops(1) .assertContents("test123"); From 567523d82d979a4f2b1c4685a7cf1d561c176cc3 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Mon, 15 May 2023 13:22:23 -0700 Subject: [PATCH 07/12] fix test --- .../kyori/adventure/text/flattener/ComponentFlattenerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java b/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java index c6437ee7c..f8e716e70 100644 --- a/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java +++ b/api/src/test/java/net/kyori/adventure/text/flattener/ComponentFlattenerTest.java @@ -215,7 +215,7 @@ void testVirtualComponent() { this.testFlatten(ComponentFlattener.basic(), Component.virtual(Object.class, context -> Component.text("test123"))) .assertBalanced() .assertPushesAndPops(1) - .assertContents("test123"); + .assertContents(""); // cannot get rendered value as we don't have a context available } @Test From 7ee6b1eba56267c85d2a65d795e5cd2f87a33aab Mon Sep 17 00:00:00 2001 From: Riley Park Date: Mon, 15 May 2023 13:24:56 -0700 Subject: [PATCH 08/12] a --- .../net/kyori/adventure/text/Component.java | 32 +++++++++---------- .../adventure/text/VirtualComponent.java | 2 +- .../adventure/text/VirtualComponentImpl.java | 26 +++++++-------- .../renderer/AbstractComponentRenderer.java | 2 +- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/api/src/main/java/net/kyori/adventure/text/Component.java b/api/src/main/java/net/kyori/adventure/text/Component.java index 2a5ee3584..3ad538dd5 100644 --- a/api/src/main/java/net/kyori/adventure/text/Component.java +++ b/api/src/main/java/net/kyori/adventure/text/Component.java @@ -1267,67 +1267,67 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex * Creates a virtual component. * * @param the context type - * @param context the context type + * @param contextType the context type * @param renderer the renderer * @return a virtual component * @since 4.14.0 */ @Contract(value = "_, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer) { - requireNonNull(context, "context"); + static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer) { + requireNonNull(contextType, "context type"); requireNonNull(renderer, "renderer"); - return VirtualComponentImpl.createVirtual(context, renderer); + return VirtualComponentImpl.createVirtual(contextType, renderer); } /** * Creates a virtual component with a value. * * @param the context type - * @param context the context type + * @param contextType the context type * @param renderer the renderer * @param style the style * @return a virtual component * @since 4.14.0 */ @Contract(value = "_, _, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final @NotNull Style style) { - requireNonNull(context, "context"); + static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final @NotNull Style style) { + requireNonNull(contextType, "context type"); requireNonNull(renderer, "renderer"); - return VirtualComponentImpl.createVirtual(context, renderer, Collections.emptyList(), style); + return VirtualComponentImpl.createVirtual(contextType, renderer, Collections.emptyList(), style); } /** * Creates a virtual component with a value. * * @param the context type - * @param context the context type + * @param contextType the context type * @param renderer the renderer * @param style the style elements * @return a virtual component * @since 4.14.0 */ @Contract(value = "_, _, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final @NotNull StyleBuilderApplicable... style) { - requireNonNull(context, "context"); + static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final @NotNull StyleBuilderApplicable... style) { + requireNonNull(contextType, "context type"); requireNonNull(renderer, "renderer"); - return VirtualComponentImpl.createVirtual(context, renderer, Collections.emptyList(), Style.style(style)); + return VirtualComponentImpl.createVirtual(contextType, renderer, Collections.emptyList(), Style.style(style)); } /** * Creates a virtual component with a value. * * @param the context type - * @param context the context type + * @param contextType the context type * @param renderer the renderer * @param style the style elements * @return a virtual component * @since 4.14.0 */ @Contract(value = "_, _, _ -> new", pure = true) - static @NotNull VirtualComponent virtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final @NotNull Iterable style) { - requireNonNull(context, "context"); + static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final @NotNull Iterable style) { + requireNonNull(contextType, "context type"); requireNonNull(renderer, "renderer"); - return VirtualComponentImpl.createVirtual(context, renderer, Collections.emptyList(), Style.style(style)); + return VirtualComponentImpl.createVirtual(contextType, renderer, Collections.emptyList(), Style.style(style)); } /* diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java index d057c051f..f28dda0d5 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java @@ -39,7 +39,7 @@ public interface VirtualComponent extends TextComponent { * @return the renderer context type * @since 4.14.0 */ - @NotNull Class context(); + @NotNull Class contextType(); /** * Gets the renderer. diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java index 511ee354b..385c8d0d4 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java @@ -29,33 +29,33 @@ import org.jetbrains.annotations.NotNull; final class VirtualComponentImpl extends TextComponentImpl implements VirtualComponent { - static VirtualComponent createVirtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer) { - return createVirtual(context, renderer, Collections.emptyList(), Style.empty()); + static VirtualComponent createVirtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer) { + return createVirtual(contextType, renderer, Collections.emptyList(), Style.empty()); } - static VirtualComponent createVirtual(final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer, final List children, final Style style) { + static VirtualComponent createVirtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final List children, final Style style) { final List filteredChildren = ComponentLike.asComponents(children, IS_NOT_EMPTY); - return new VirtualComponentImpl(filteredChildren, style, "", context, renderer); + return new VirtualComponentImpl(filteredChildren, style, "", contextType, renderer); } - private final Class context; + private final Class contextType; private final VirtualComponentRenderer renderer; - private VirtualComponentImpl(final @NotNull List children, final @NotNull Style style, final @NotNull String content, final @NotNull Class context, final @NotNull VirtualComponentRenderer renderer) { + private VirtualComponentImpl(final @NotNull List children, final @NotNull Style style, final @NotNull String content, final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer) { super(children, style, content); - this.context = context; + this.contextType = contextType; this.renderer = renderer; } @Override VirtualComponent create0(final @NotNull List children, final @NotNull Style style, final @NotNull String content) { - return new VirtualComponentImpl(ComponentLike.asComponents(children, IS_NOT_EMPTY), style, content, this.context, this.renderer); + return new VirtualComponentImpl(ComponentLike.asComponents(children, IS_NOT_EMPTY), style, content, this.contextType, this.renderer); } @Override - public @NotNull Class context() { - return this.context; + public @NotNull Class contextType() { + return this.contextType; } @Override @@ -74,18 +74,18 @@ VirtualComponent create0(final @NotNull List children, } static final class BuilderImpl extends TextComponentImpl.BuilderImpl { - private final Class context; + private final Class contextType; private final VirtualComponentRenderer renderer; BuilderImpl(final VirtualComponent other) { super(other); - this.context = other.context(); + this.contextType = other.contextType(); this.renderer = other.renderer(); } @Override public @NotNull TextComponent build() { - return createVirtual(this.context, this.renderer, this.children, this.buildStyle()); + return createVirtual(this.contextType, this.renderer, this.children, this.buildStyle()); } } } diff --git a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java index 4704b9bb7..51aa83d50 100644 --- a/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java +++ b/api/src/main/java/net/kyori/adventure/text/renderer/AbstractComponentRenderer.java @@ -144,7 +144,7 @@ public abstract class AbstractComponentRenderer implements ComponentRenderer< */ @SuppressWarnings("unchecked") protected @NotNull Component renderVirtual(final @NotNull VirtualComponent component, final @NotNull C context) { - if (component.context().isInstance(context)) { + if (component.contextType().isInstance(context)) { return ((VirtualComponentRenderer) component.renderer()).apply(context).asComponent(); } return component; // will be processed as a TextComponent instead From 1cabc45ac58f0fa73aefd349c0d4632e043d469d Mon Sep 17 00:00:00 2001 From: zml Date: Fri, 16 Feb 2024 15:15:57 +0100 Subject: [PATCH 09/12] update MM implementation for changes --- .../standard/AbstractColorChangingTag.java | 42 ++++++++----------- .../text/minimessage/AbstractTest.java | 9 +++- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java index a45877ce0..91da16bbd 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java @@ -29,9 +29,10 @@ import java.util.stream.Stream; import net.kyori.adventure.internal.Internals; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.VirtualComponent; -import net.kyori.adventure.text.VirtualComponentHolder; +import net.kyori.adventure.text.VirtualComponentRenderer; import net.kyori.adventure.text.flattener.ComponentFlattener; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.minimessage.internal.parser.node.TagNode; @@ -45,6 +46,7 @@ import net.kyori.examination.ExaminableProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; /** * A transformation that applies a colour change. @@ -102,7 +104,7 @@ public final void postVisit() { public final Component apply(final @NotNull Component current, final int depth) { if (depth == 0) { // capture state into a virtual component, no other logic is needed in normal MM handling - return Component.virtual(new TagInfoHolder(new ComponentData(this.preserveData(), current))); + return Component.virtual(Void.class, new TagInfoHolder(this.preserveData(), current)); } if ((this.disableApplyingColorDepth != -1 && depth > this.disableApplyingColorDepth) || current.style().color() != null) { @@ -198,41 +200,33 @@ private void skipColorForLengthOf(final String content) { @Override public abstract int hashCode(); - static final class ComponentData implements Emitable { - final Consumer output; - final Component originalComp; + static final class TagInfoHolder implements VirtualComponentRenderer, Emitable { + private final Consumer output; + private final Component originalComp; - ComponentData(final Consumer output, final Component originalComp) { + TagInfoHolder(final Consumer output, final Component originalComp) { this.output = output; this.originalComp = originalComp; } @Override - public void emit(final @NotNull TokenEmitter emitter) { - this.output.accept(emitter); - } - - @Override - public Component substitute() { + public @UnknownNullability ComponentLike apply(final @NotNull Void context) { return this.originalComp; } - } - static final class TagInfoHolder implements VirtualComponentHolder { - private final ComponentData data; - - TagInfoHolder(final ComponentData data) { - this.data = data; + @Override + public @NotNull String fallbackString() { + return ""; // only holds data for reserialization, not for display } @Override - public @NotNull ComponentData unbox() { - return this.data; + public void emit(final @NotNull TokenEmitter emitter) { + this.output.accept(emitter); } @Override - public @NotNull String fallbackString() { - return ""; // only holds data for reserialization, not for display + public @Nullable Component substitute() { + return this.originalComp; } } @@ -241,11 +235,11 @@ static final class TagInfoHolder implements VirtualComponentHolder holder = ((VirtualComponent) comp).holder(); + final VirtualComponentRenderer holder = ((VirtualComponent) comp).renderer(); if (!(holder instanceof TagInfoHolder)) { return null; } - return ((TagInfoHolder) holder).unbox(); + return (TagInfoHolder) holder; } } diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java index 06451ad1c..ad1e8dd1b 100644 --- a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/AbstractTest.java @@ -29,11 +29,13 @@ import java.util.stream.Collectors; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.VirtualComponentRenderer; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.examination.string.MultiLineStringExaminer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnknownNullability; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -78,7 +80,12 @@ public static ArgumentQueue emptyArgumentQueue(final Context context) { } public static Component virtualOfChildren(final ComponentLike... children) { - return Component.virtual(() -> "") // not part of equality... should it be? + return Component.virtual(Void.class, new VirtualComponentRenderer() { + @Override + public @UnknownNullability ComponentLike apply(final @NotNull Void context) { + return Component.empty(); + } + }) // not part of equality... should it be? .children(Arrays.asList(children)); } } From 336905ac714b0e08e3bb623e0e89d3e2bdd47c7c Mon Sep 17 00:00:00 2001 From: zml Date: Fri, 16 Feb 2024 15:57:15 +0100 Subject: [PATCH 10/12] chore(text-minimessage): Update license header for PR --- .../main/java/net/kyori/adventure/text/VirtualComponent.java | 2 +- .../java/net/kyori/adventure/text/VirtualComponentImpl.java | 2 +- .../java/net/kyori/adventure/text/VirtualComponentRenderer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java index f28dda0d5..3f52b67b7 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2023 KyoriPowered + * Copyright (c) 2017-2024 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java index 385c8d0d4..085fa0c27 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentImpl.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2023 KyoriPowered + * Copyright (c) 2017-2024 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java index 57802c5a8..2d0adb106 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2023 KyoriPowered + * Copyright (c) 2017-2024 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From fa0fdce9cd2ccf4ef539250c595e1ad6221f9b5b Mon Sep 17 00:00:00 2001 From: zml Date: Fri, 16 Feb 2024 21:34:20 +0100 Subject: [PATCH 11/12] fix(text-minimessage): Pass through style on virtual components --- .../minimessage/tag/standard/AbstractColorChangingTag.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java index 91da16bbd..9bc33f57e 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/standard/AbstractColorChangingTag.java @@ -61,7 +61,6 @@ * @since 4.10.0 */ abstract class AbstractColorChangingTag implements Modifying, Examinable { - private static final ComponentFlattener LENGTH_CALCULATOR = ComponentFlattener.builder() .mapper(TextComponent.class, TextComponent::content) .unknownMapper(x -> "_") // every unknown component gets a single colour @@ -104,7 +103,7 @@ public final void postVisit() { public final Component apply(final @NotNull Component current, final int depth) { if (depth == 0) { // capture state into a virtual component, no other logic is needed in normal MM handling - return Component.virtual(Void.class, new TagInfoHolder(this.preserveData(), current)); + return Component.virtual(Void.class, new TagInfoHolder(this.preserveData(), current), current.style()); } if ((this.disableApplyingColorDepth != -1 && depth > this.disableApplyingColorDepth) || current.style().color() != null) { From b88b00b6bd2886feb4084f2b78c2ad416f2bc53b Mon Sep 17 00:00:00 2001 From: zml Date: Fri, 16 Feb 2024 21:47:39 +0100 Subject: [PATCH 12/12] chore(api): update since tags for virtual components --- api/src/main/java/net/kyori/adventure/text/Component.java | 8 ++++---- .../java/net/kyori/adventure/text/VirtualComponent.java | 6 +++--- .../kyori/adventure/text/VirtualComponentRenderer.java | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/net/kyori/adventure/text/Component.java b/api/src/main/java/net/kyori/adventure/text/Component.java index 3ad538dd5..ffff4edef 100644 --- a/api/src/main/java/net/kyori/adventure/text/Component.java +++ b/api/src/main/java/net/kyori/adventure/text/Component.java @@ -1270,7 +1270,7 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex * @param contextType the context type * @param renderer the renderer * @return a virtual component - * @since 4.14.0 + * @since 4.16.0 */ @Contract(value = "_, _ -> new", pure = true) static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer) { @@ -1287,7 +1287,7 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex * @param renderer the renderer * @param style the style * @return a virtual component - * @since 4.14.0 + * @since 4.16.0 */ @Contract(value = "_, _, _ -> new", pure = true) static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final @NotNull Style style) { @@ -1304,7 +1304,7 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex * @param renderer the renderer * @param style the style elements * @return a virtual component - * @since 4.14.0 + * @since 4.16.0 */ @Contract(value = "_, _, _ -> new", pure = true) static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final @NotNull StyleBuilderApplicable... style) { @@ -1321,7 +1321,7 @@ public interface Component extends ComponentBuilderApplicable, ComponentLike, Ex * @param renderer the renderer * @param style the style elements * @return a virtual component - * @since 4.14.0 + * @since 4.16.0 */ @Contract(value = "_, _, _ -> new", pure = true) static @NotNull VirtualComponent virtual(final @NotNull Class contextType, final @NotNull VirtualComponentRenderer renderer, final @NotNull Iterable style) { diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java index 3f52b67b7..9448c23f7 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponent.java @@ -30,14 +30,14 @@ * *

This component type is transient, and not guaranteed to survive during any sort of transformations or serialization.

* - * @since 4.14.0 + * @since 4.16.0 */ public interface VirtualComponent extends TextComponent { /** * Gets the renderer context type. * * @return the renderer context type - * @since 4.14.0 + * @since 4.16.0 */ @NotNull Class contextType(); @@ -45,7 +45,7 @@ public interface VirtualComponent extends TextComponent { * Gets the renderer. * * @return the renderer - * @since 4.14.0 + * @since 4.16.0 */ @NotNull VirtualComponentRenderer renderer(); } diff --git a/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java b/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java index 2d0adb106..38651ede9 100644 --- a/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java +++ b/api/src/main/java/net/kyori/adventure/text/VirtualComponentRenderer.java @@ -30,7 +30,7 @@ * A holder for a value. * * @param the context type - * @since 4.14.0 + * @since 4.16.0 */ public interface VirtualComponentRenderer { /** @@ -38,7 +38,7 @@ public interface VirtualComponentRenderer { * * @param context the context * @return the rendered value - * @since 4.14.0 + * @since 4.16.0 */ @UnknownNullability ComponentLike apply(final @NotNull C context); @@ -48,7 +48,7 @@ public interface VirtualComponentRenderer { *

By default, this will be an empty string.

* * @return the fallback string - * @since 4.14.0 + * @since 4.16.0 */ default @NotNull String fallbackString() { return "";