diff --git a/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java b/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java index 779a284c..e8b8c41f 100644 --- a/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java +++ b/eco-api/src/main/java/com/willfp/eco/util/StringUtils.java @@ -10,6 +10,8 @@ import com.willfp.eco.core.Eco; import com.willfp.eco.core.integrations.placeholder.PlaceholderManager; import com.willfp.eco.core.placeholder.context.PlaceholderContext; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; +import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -784,6 +786,58 @@ public final class StringUtils { return result.toString(); } + /** + * Line wrap a string while preserving formatting. + * + * @param input The input string. + * @param lineLength The length of each line. + * @return The wrapped string. + */ + @NotNull + public static List lineWrap(@NotNull final String input, + final int lineLength) { + Component asComponent = toComponent(input); + + // The component contains the text as its children, so the child components + // are accessed like this: + List children = new ArrayList<>(); + + if (asComponent instanceof TextComponent) { + children.add((TextComponent) asComponent); + } + + for (Component child : asComponent.children()) { + children.add((TextComponent) child); + } + + // Start by splitting the component into individual characters. + List letters = new ArrayList<>(); + for (TextComponent child : children) { + for (char c : child.content().toCharArray()) { + letters.add(Component.text(c).mergeStyle(child)); + } + } + + List lines = new ArrayList<>(); + List currentLine = new ArrayList<>(); + + for (TextComponent letter : letters) { + if (currentLine.size() > lineLength && letter.content().isBlank()) { + lines.add(Component.join(JoinConfiguration.noSeparators(), currentLine)); + currentLine.clear(); + } else { + currentLine.add(letter); + } + } + + // Push last line. + lines.add(Component.join(JoinConfiguration.noSeparators(), currentLine)); + + // Convert back to legacy strings. + return lines.stream().map(StringUtils::toLegacy) + .collect(Collectors.toList()); + } + /** * Options for formatting. */