diff --git a/src/main/java/de/themoep/minedown/adventure/MineDownParser.java b/src/main/java/de/themoep/minedown/adventure/MineDownParser.java index e76a7d6..27735d6 100644 --- a/src/main/java/de/themoep/minedown/adventure/MineDownParser.java +++ b/src/main/java/de/themoep/minedown/adventure/MineDownParser.java @@ -137,30 +137,14 @@ public ComponentBuilder parse(String message) throws IllegalArgumentException { boolean isEscape = c == '\\' && i + 1 < message.length(); boolean isColorCode = isEnabled(Option.LEGACY_COLORS) && i + 1 < message.length() && (c == 'ยง' || c == colorChar()); - boolean isEvent = false; - if (isEnabled(Option.ADVANCED_FORMATTING) && c == '[') { - int nextEventClose = Util.indexOfNotEscaped(message, "](", i + 1); - if (nextEventClose != -1 && nextEventClose + 2 < message.length()) { - int nextDefClose = Util.indexOfNotEscaped(message, ")", i + 2); - if (nextDefClose != -1) { - int depth = 1; - isEvent = true; - boolean innerEscaped = false; - for (int j = i + 1; j < nextEventClose; j++) { - if (innerEscaped) { - innerEscaped = false; - } else if (message.charAt(j) == '\\') { - innerEscaped = true; - } else if (message.charAt(j) == '[') { - depth++; - } else if (message.charAt(j) == ']') { - depth--; - } - if (depth == 0) { - isEvent = false; - break; - } - } + int eventEndIndex = -1; + String eventDefinition = ""; + if (!escaped && isEnabled(Option.ADVANCED_FORMATTING) && c == '[') { + eventEndIndex = Util.getUnescapedEndIndex(message, '[', ']', i); + if (eventEndIndex != -1 && message.length() > eventEndIndex + 1 && message.charAt(eventEndIndex + 1) == '(') { + int definitionClose = Util.getUnescapedEndIndex(message, '(', ')', eventEndIndex + 1); + if (definitionClose != -1) { + eventDefinition = message.substring(eventEndIndex + 2, definitionClose); } } } @@ -280,16 +264,14 @@ public ComponentBuilder parse(String message) throws IllegalArgumentException { continue; // Events - } else if (isEvent) { - int index = Util.indexOfNotEscaped(message, "](", i + 1); - int endIndex = Util.indexOfNotEscaped(message, ")", index + 2); + } else if (eventEndIndex != -1 && !eventDefinition.isEmpty()) { appendValue(); if (!isFiltered(Option.ADVANCED_FORMATTING)) { - append(parseEvent(message.substring(i + 1, index), message.substring(index + 2, endIndex))); + append(parseEvent(message.substring(i + 1, eventEndIndex), eventDefinition)); } else { - append(copy(true).parse(message.substring(i + 1, index))); + append(copy(true).parse(message.substring(i + 1, eventEndIndex))); } - i = endIndex; + i = eventEndIndex + 2 + eventDefinition.length(); continue; // Simple formatting diff --git a/src/main/java/de/themoep/minedown/adventure/Util.java b/src/main/java/de/themoep/minedown/adventure/Util.java index 1c46c05..c41d8df 100644 --- a/src/main/java/de/themoep/minedown/adventure/Util.java +++ b/src/main/java/de/themoep/minedown/adventure/Util.java @@ -183,6 +183,34 @@ public static boolean isEscaped(String string, int index) { return e % 2 != 0; } + /** + * Gets the proper end index of a certain definition on the same depth while ignoring escaped chars. + * @param string The string to search + * @param startChar The start cahracter of the definition + * @param endChar The end character of the definition + * @param fromIndex The index to start searching from (should be at the start char) + * @return The first end index of that group or {@code -1} if not found + */ + public static int getUnescapedEndIndex(String string, char startChar, char endChar, int fromIndex) { + int depth = 0; + boolean innerEscaped = false; + for (int i = fromIndex; i < string.length(); i++) { + if (innerEscaped) { + innerEscaped = false; + } else if (string.charAt(i) == '\\') { + innerEscaped = true; + } else if (string.charAt(i) == startChar) { + depth++; + } else if (string.charAt(i) == endChar) { + depth--; + if (depth == 0) { + return i; + } + } + } + return -1; + } + /** * Wrap a string if it is longer than the line length and contains no new line. * Will try to wrap at spaces between words. diff --git a/src/test/java/de/themoep/minedown/adventure/tests/ParserTest.java b/src/test/java/de/themoep/minedown/adventure/tests/ParserTest.java index a3b9c59..c7f7215 100644 --- a/src/test/java/de/themoep/minedown/adventure/tests/ParserTest.java +++ b/src/test/java/de/themoep/minedown/adventure/tests/ParserTest.java @@ -135,4 +135,13 @@ public void testNegated() { () -> parse("&lBold [not bold](!bold) bold") ); } + + @Test + public void testParseNested() { + Assertions.assertAll( + () -> parse("[outer start [inner](green) outer end](aqua)"), + () -> parse("[outer start \\[[inner](green)\\] outer end](aqua)"), + () -> parse("[outer start [inner](green) outer end](aqua hover={[red hover](red)})") + ); + } }