diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageParser.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageParser.java index fab96f608..4dd3f59bf 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageParser.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/MiniMessageParser.java @@ -98,7 +98,7 @@ private void processTokens(final @NotNull StringBuilder sb, final @NotNull Conte private void processTokens(final @NotNull StringBuilder sb, final @NotNull String richMessage, final @NotNull ContextImpl context, final BiConsumer tagHandler) { final TagResolver combinedResolver = TagResolver.resolver(this.tagResolver, context.extraTags()); - final List root = TokenParser.tokenize(richMessage); + final List root = TokenParser.tokenize(richMessage, true); for (final Token token : root) { switch (token.type()) { case TEXT: diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/TokenParser.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/TokenParser.java index 040741ca7..34f40adc1 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/TokenParser.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/TokenParser.java @@ -86,7 +86,7 @@ public static RootNode parse( final boolean strict ) throws ParsingException { // collect tokens... - final List tokens = tokenize(message); + final List tokens = tokenize(message, false); // then build the tree! return buildTree(tagProvider, tagNameChecker, tokens, message, originalMessage, strict); @@ -109,7 +109,7 @@ public static String resolvePreProcessTags(final String message, final TagProvid lastResult = result; final StringResolvingMatchedTokenConsumer stringTokenResolver = new StringResolvingMatchedTokenConsumer(lastResult, provider); - parseString(lastResult, stringTokenResolver); + parseString(lastResult, false, stringTokenResolver); result = stringTokenResolver.result(); passes++; } while (passes < MAX_DEPTH && !lastResult.equals(result)); @@ -121,12 +121,13 @@ public static String resolvePreProcessTags(final String message, final TagProvid * Tokenize a minimessage string into a list of tokens. * * @param message the minimessage string to parse + * @param lenient whether to allow section symbols (for escaping/stripping/non-actual-parse stuff only) * @return the root tokens * @since 4.10.0 */ - public static List tokenize(final String message) { + public static List tokenize(final String message, final boolean lenient) { final TokenListProducingMatchedTokenConsumer listProducer = new TokenListProducingMatchedTokenConsumer(message); - parseString(message, listProducer); + parseString(message, lenient, listProducer); final List tokens = listProducer.result(); parseSecondPass(message, tokens); return tokens; @@ -142,10 +143,11 @@ enum FirstPassState { * Parses a string, providing information on matched tokens to the matched token consumer. * * @param message the message + * @param lenient whether to allow section symbols * @param consumer the consumer * @since 4.10.0 */ - public static void parseString(final String message, final MatchedTokenConsumer consumer) { + public static void parseString(final String message, final boolean lenient, final MatchedTokenConsumer consumer) { FirstPassState state = FirstPassState.NORMAL; // If the current state is escaped then the next character is skipped boolean escaped = false; @@ -158,7 +160,7 @@ public static void parseString(final String message, final MatchedTokenConsumer< final int length = message.length(); for (int i = 0; i < length; i++) { final int codePoint = message.codePointAt(i); - if (codePoint == '§' && i + 1 < length) { + if (!lenient && codePoint == '§' && i + 1 < length) { final int nextChar = Character.toLowerCase(message.codePointAt(i + 1)); // Only throw an exception if the next character is actually going to make a legacy color code if ((nextChar >= '0' && nextChar <= '9') diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/match/StringResolvingMatchedTokenConsumer.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/match/StringResolvingMatchedTokenConsumer.java index ca7664e09..120bbc0be 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/match/StringResolvingMatchedTokenConsumer.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/internal/parser/match/StringResolvingMatchedTokenConsumer.java @@ -82,7 +82,7 @@ public void accept(final int start, final int end, final @NotNull TokenType toke // we might care if it's a valid tag! if (TagInternals.sanitizeAndCheckValidTagName(tag)) { - final List tokens = tokenize(match); + final List tokens = tokenize(match, false); final List parts = new ArrayList<>(); final List childs = tokens.isEmpty() ? null : tokens.get(0).childTokens(); if (childs != null) { diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/MiniMessageParserTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/MiniMessageParserTest.java index bff3456d4..b1c7af855 100644 --- a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/MiniMessageParserTest.java +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/MiniMessageParserTest.java @@ -157,6 +157,15 @@ void testEscapeParse() { assertEquals(expected, PlainTextComponentSerializer.plainText().serialize(comp)); } + // https://github.com/KyoriPowered/adventure/issues/799 + @Test + void testEscapeIgnoresSectionSigns() { + final String input = "Hello, read §64 for information"; + final String expected = "Hello, read §64 for \\ information"; + + assertEquals(expected, PARSER.escapeTags(input)); + } + @Test void testNiceMix() { final String input = " random strangerclick here to FEEL it";