Skip to content

Commit

Permalink
fix(text-minimessage): Properly handle escaped tag opens
Browse files Browse the repository at this point in the history
Fixes GH-821
  • Loading branch information
zml2008 committed Nov 7, 2022
1 parent d82d5ac commit ccf452d
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 8 deletions.
Expand Up @@ -24,6 +24,7 @@
package net.kyori.adventure.text.minimessage.internal.parser;

import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import net.kyori.adventure.internal.Internals;
import net.kyori.examination.Examinable;
Expand Down Expand Up @@ -126,6 +127,19 @@ public CharSequence get(final CharSequence message) {
);
}

@Override
public boolean equals(final Object other) {
if (this == other) return true;
if (!(other instanceof Token)) return false;
final Token that = (Token) other;
return this.startIndex == that.startIndex && this.endIndex == that.endIndex && this.type == that.type;
}

@Override
public int hashCode() {
return Objects.hash(this.startIndex, this.endIndex, this.type);
}

@Override
public String toString() {
return Internals.toString(this);
Expand Down
Expand Up @@ -193,6 +193,11 @@ public static void parseString(final String message, final MatchedTokenConsumer<
escaped = currentStringChar == nextCodePoint || nextCodePoint == ESCAPE;
break;
case TAG:
// Escape characters are not valid in tag names, so we aren't a tag token
if (nextCodePoint == TAG_START) {
escaped = true;
state = FirstPassState.NORMAL;
}
break;
}

Expand Down
Expand Up @@ -23,9 +23,14 @@
*/
package net.kyori.adventure.text.minimessage;

import java.util.Collections;
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.internal.parser.Token;
import net.kyori.adventure.text.minimessage.internal.parser.TokenParser;
import net.kyori.adventure.text.minimessage.internal.parser.TokenType;
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.Placeholder;
Expand Down Expand Up @@ -54,6 +59,7 @@
import static net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.parsed;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class MiniMessageParserTest extends AbstractTest {
Expand Down Expand Up @@ -300,6 +306,21 @@ void testNonTerminatingQuote() {
this.assertParsedEquals(expected4, input4);
}

// https://github.com/KyoriPowered/adventure/issues/821
@Test
void testEscapeIncompleteTags() {
final String input = "<<aqua> a";
final String escaped = PARSER.escapeTags(input);

assertEquals("<\\<aqua> a", escaped);

final List<Token> expectedTokens = Collections.singletonList(new Token(0, escaped.length(), TokenType.TEXT));
assertIterableEquals(expectedTokens, TokenParser.tokenize(escaped));

final Component expected = text("<<aqua> a");
this.assertParsedEquals(expected, escaped);
}

// GH-68, GH-93
@Test
void testAngleBracketsShit() {
Expand Down
Expand Up @@ -29,18 +29,19 @@
import net.kyori.adventure.text.format.TextDecoration;
import org.junit.jupiter.api.Test;

import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.Style.style;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class MiniMessageSerializerTest extends AbstractTest {
@Test
void testSerializeNestedStyles() {
// These are mostly arbitrary, but I don't want to test every single combination
final Component component = Component.text()
.append(Component.text("b+i+u", style(TextDecoration.BOLD, TextDecoration.ITALIC, TextDecoration.UNDERLINED)))
.append(Component.text("color+insert", style(NamedTextColor.RED)).insertion("meow"))
.append(Component.text("st+font", style(TextDecoration.STRIKETHROUGH).font(Key.key("uniform"))))
.append(Component.text("empty"))
final Component component = text()
.append(text("b+i+u", style(TextDecoration.BOLD, TextDecoration.ITALIC, TextDecoration.UNDERLINED)))
.append(text("color+insert", style(NamedTextColor.RED)).insertion("meow"))
.append(text("st+font", style(TextDecoration.STRIKETHROUGH).font(Key.key("uniform"))))
.append(text("empty"))
.build();
final String expected = "<italic><underlined><bold>b+i+u</bold></underlined></italic>" +
"<insert:meow><red>color+insert</red></insert>" +
Expand All @@ -56,16 +57,25 @@ void testTagsClosedInStrictMode() {
final MiniMessage serializer = MiniMessage.builder().strict(true).build();

final String expected = "hello<red>red<bold>bold</bold></red>";
final Component input = Component.text()
final Component input = text()
.content("hello")
.append(
Component.text("red", NamedTextColor.RED)
text("red", NamedTextColor.RED)
.append(
Component.text("bold", style(TextDecoration.BOLD))
text("bold", style(TextDecoration.BOLD))
)
)
.build();

assertEquals(expected, serializer.serialize(input));
}

@Test
void testDoubleOpenRoundTrippedEscaped() {
final String expected = "\\<\\<aqua> a"; // this is valid but there is a redundant serialization
final Component component = text("<<aqua> a");
this.assertSerializedEquals(expected, component);
this.assertParsedEquals(component, expected);
}

}

0 comments on commit ccf452d

Please sign in to comment.