diff --git a/src/main/java/me/blvckbytes/item_predicate_parser/parse/SyllablesMatcher.java b/src/main/java/me/blvckbytes/item_predicate_parser/parse/SyllablesMatcher.java index 0c768b4..7448983 100644 --- a/src/main/java/me/blvckbytes/item_predicate_parser/parse/SyllablesMatcher.java +++ b/src/main/java/me/blvckbytes/item_predicate_parser/parse/SyllablesMatcher.java @@ -219,6 +219,14 @@ private boolean matchQueryAgainstTargets(int querySyllable, Syllables target, lo } private void addTargetRemainder(int start, int end) { + assert target != null; + + // Mini-Message's *ingenious* parser colors whitespace on - for example - rainbows, thus we + // need to check for whether the syllable is just made up of a color-sequence, as to not + // have matches fail because of dangling colors. + if (isColorSequence(target.container, start, end) == (end - start + 1)) + return; + targetRemainders.add(start, end, false); int newRequiredLongs = requiredLongs(targetRemainders.capacity()); @@ -230,6 +238,62 @@ private void addTargetRemainder(int start, int end) { } } + /** + * @return Length of color-sequence at that position, zero if there is none + */ + private int isColorSequence(String input, int position, int lastPosition) { + if (input.charAt(position) != '§') + return 0; + + var remainingCharacters = lastPosition - position + 1; + + // Need to have at least two characters to be a color-sequence + if (remainingCharacters == 1) + return 0; + + var nextContainerChar = input.charAt(position + 1); + + // Standard color-sequences + if ( + (nextContainerChar >= '0' && nextContainerChar <= '9') || + (nextContainerChar >= 'a' && nextContainerChar <= 'f') || + (nextContainerChar >= 'k' && nextContainerChar <= 'o') || + nextContainerChar == 'r' + ) { + return 2; + } + + // Hex color-sequences of format §x§§§§§§ + if (nextContainerChar == 'x' && remainingCharacters >= 2 + 6 * 2) { + var matchesSequence = true; + + for (var i = 1; i <= 6 * 2; i += 2) { + var paragraphChar = input.charAt(position + 1 + i); + var hexColorChar = input.charAt(position + 1 + (i + 1)); + + if ( + paragraphChar == '§' && + ( + Character.isDigit(hexColorChar) || + (hexColorChar >= 'a' && hexColorChar <= 'f') || + (hexColorChar >= 'A' && hexColorChar <= 'F') + ) + ) { + continue; + } + + matchesSequence = false; + break; + } + + if (matchesSequence) + return 2 + 6 * 2; + } + + // Not a color-sequence of any known scheme + return 0; + } + /** * @return <32b begin_in_target><32b number_of_target_chars>; * if begin_in_target == Integer.MAX_VALUE then it's not contained; @@ -247,6 +311,7 @@ private long relativeIndexOf(int querySyllable, int targetSyllable) { var querySyllableStart = Syllables.getStartIndex(querySyllable); var targetSyllableStart = Syllables.getStartIndex(targetSyllable); + var targetSyllableEnd = Syllables.getEndIndex(targetSyllable); /* 0: A B C @@ -268,26 +333,15 @@ private long relativeIndexOf(int querySyllable, int targetSyllable) { for (int queryOffset = 0; queryOffset < querySyllableLength; ++queryOffset) { var targetIndex = targetSyllableStart + queryOffset + targetOffset; var queryIndex = querySyllableStart + queryOffset; - var targetChar = target.container.charAt(targetIndex); - while (targetChar == '§' && targetOffset < highestOffset) { - var nextContainerChar = target.container.charAt(targetIndex + 1); - - if ( - (nextContainerChar >= '0' && nextContainerChar <= '9') - || (nextContainerChar >= 'a' && nextContainerChar <= 'f') - || (nextContainerChar >= 'k' && nextContainerChar <= 'o') - || nextContainerChar == 'r' - ) { - targetOffset += 2; - targetIndex = targetSyllableStart + queryOffset + targetOffset; - targetChar = target.container.charAt(targetIndex); - } - - else - break; + int colorSequenceLength; + + while ((colorSequenceLength = isColorSequence(target.container, targetIndex, targetSyllableEnd)) != 0) { + targetOffset += colorSequenceLength; + targetIndex += colorSequenceLength; } + var targetChar = target.container.charAt(targetIndex); var containedChar = query.container.charAt(queryIndex); if (charToLower(targetChar) != charToLower(containedChar)) { @@ -296,8 +350,10 @@ private long relativeIndexOf(int querySyllable, int targetSyllable) { } } - if (didMatch) - return ((long) initialTargetOffset) << 16 | (targetOffset - initialTargetOffset + querySyllableLength); + if (didMatch) { + var numberOfTargetChars = targetOffset - initialTargetOffset + querySyllableLength; + return ((long) initialTargetOffset) << 16 | numberOfTargetChars; + } } return ((long) Integer.MAX_VALUE) << 16; diff --git a/src/test/java/me/blvckbytes/item_predicate_parser/SyllableTests.java b/src/test/java/me/blvckbytes/item_predicate_parser/SyllableTests.java index d185564..c383484 100644 --- a/src/test/java/me/blvckbytes/item_predicate_parser/SyllableTests.java +++ b/src/test/java/me/blvckbytes/item_predicate_parser/SyllableTests.java @@ -374,6 +374,39 @@ public void shouldIgnoreStandardColorSequences() { ); } + @Test + public void shouldIgnoreHexColorSequences() { + var coloredString = "§x§F§F§0§0§0§0h§x§F§F§5§F§0§0e§x§F§F§B§F§0§0l§x§D§F§F§F§0§0l§x§7§F§F§F§0§0o§x§1§F§F§F§0§0,§x§0§0§F§F§3§F-§x§0§0§F§F§9§Fw§x§0§0§F§F§F§Fo§x§0§0§9§F§F§Fr§x§0§0§3§F§F§Fl§x§1§F§0§0§F§Fd§x§7§F§0§0§F§F!§x§D§F§0§0§F§F-§x§F§F§0§0§B§F:§x§F§F§0§0§5§F)"; + + makeUnmatchedCase( + coloredString, "hello,-world!-:)", false, + EMPTY_SYLLABLES, + EMPTY_SYLLABLES + ); + } + + @Test + public void shouldIgnoreConsecutiveStandardColorSequences() { + var coloredString = "§c§l§kHello-world"; + + makeUnmatchedCase( + coloredString, "hello-world", false, + EMPTY_SYLLABLES, + EMPTY_SYLLABLES + ); + } + + @Test + public void shouldIgnoreConsecutiveHexColorSequences() { + var coloredString = "§x§F§F§0§0§0§0§x§0§0§F§F§0§0§x§0§0§0§0§F§FHello-world"; + + makeUnmatchedCase( + coloredString, "hello-world", false, + EMPTY_SYLLABLES, + EMPTY_SYLLABLES + ); + } + @Test public void shouldResetMatches() { var remainingTargetSyllables = new Syllables(null)