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 60fb9528c..040741ca7 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 @@ -246,8 +246,11 @@ public static void parseString(final String message, final MatchedTokenConsumer< break; case '\'': case '"': - state = FirstPassState.STRING; currentStringChar = (char) codePoint; + // Look ahead if the quote being opened is ever closed + if (message.indexOf(codePoint, i + 1) != -1) { + state = FirstPassState.STRING; + } break; } break; @@ -257,6 +260,14 @@ public static void parseString(final String message, final MatchedTokenConsumer< } break; } + + if (i == (length - 1) && state == FirstPassState.TAG) { + // We've reached the end of the input with an open `<`, but it was never matched to a closing `>`. + // Anything which was inside of quotes needs to be parsed again, as it may contain additional tags. + // Rewind back to directly after the `<`, but in the NORMAL state, instead of TAG. + i = marker; + state = FirstPassState.NORMAL; + } } // anything left over is plain text 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 086f32e9b..bff3456d4 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 @@ -280,6 +280,26 @@ void testQuoteEscapingInArguments() { this.assertParsedEquals(expected3, input3); } + @Test + void testNonTerminatingQuote() { + final Component expected = empty().append(text("Remember the<3\"").color(RED)).append(text(" bug")); + final Component expected1 = empty().append(text("Remember the<3'").color(RED)).append(text(" bug")); + final Component expected2 = empty().append(text("Remember the \">bug").color(RED)); + final String input = "Remember the<3\" bug"; + final String input1 = "Remember the<3' bug"; + final String input2 = "Remember the bug"; + final String input3 = "Remember the \"bug"; + final String input4 = "Remember the \">bug"; + this.assertParsedEquals(expected, input); + this.assertParsedEquals(expected1, input1); + this.assertParsedEquals(expected2, input2); + this.assertParsedEquals(expected3, input3); + this.assertParsedEquals(expected4, input4); + } + // GH-68, GH-93 @Test void testAngleBracketsShit() {