diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java index 874a446a34..140aa3eef7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.chatnotifications; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.inject.Provides; import java.util.List; @@ -115,7 +116,7 @@ public class ChatNotificationsPlugin extends Plugin List items = Text.fromCSV(config.highlightWordsString()); String joined = items.stream() .map(Text::escapeJagex) // we compare these strings to the raw Jagex ones - .map(Pattern::quote) + .map(this::quoteAndIgnoreColor) // regex escape and ignore nested colors in the target message .collect(Collectors.joining("|")); // To match \b doesn't work due to <> not being in \w, // so match \b or \s @@ -184,7 +185,26 @@ public class ChatNotificationsPlugin extends Plugin while (matcher.find()) { String value = matcher.group(); - matcher.appendReplacement(stringBuffer, "" + value + ""); + + // Determine the ending color by: + // 1) use the color from value if it has one + // 2) use the last color from stringBuffer + + // To do #2 we just search for the last col tag after calling appendReplacement + String endColor = getLastColor(value); + + // Strip color tags from the highlighted region so that it remains highlighted correctly + value = stripColor(value); + + matcher.appendReplacement(stringBuffer, "' + value); + + if (endColor == null) + { + endColor = getLastColor(stringBuffer.toString()); + } + + // Append end color + stringBuffer.append(endColor == null ? "" : endColor); + update = true; found = true; } @@ -228,4 +248,61 @@ public class ChatNotificationsPlugin extends Plugin String notification = stringBuilder.toString(); notifier.notify(notification); } + + private String quoteAndIgnoreColor(String str) + { + StringBuilder stringBuilder = new StringBuilder(); + + for (int i = 0; i < str.length(); ++i) + { + char c = str.charAt(i); + stringBuilder.append(Pattern.quote(String.valueOf(c))); + stringBuilder.append("(?:]*?>)?"); + } + + return stringBuilder.toString(); + } + + /** + * Get the last color tag from a string, or null if there was none + * + * @param str + * @return + */ + private static String getLastColor(String str) + { + int colIdx = str.lastIndexOf(""); + + if (colEndIdx > colIdx) + { + // ends in a which resets the color to normal + return ""; + } + + if (colIdx == -1) + { + return null; // no color + } + + int closeIdx = str.indexOf('>', colIdx); + if (closeIdx == -1) + { + return null; // unclosed col tag + } + + return str.substring(colIdx, closeIdx + 1); // include the > + } + + /** + * Strip color tags from a string. + * + * @param str + * @return + */ + @VisibleForTesting + static String stripColor(String str) + { + return str.replaceAll("(|)", ""); + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java index ffa54ccbae..5eda571f9a 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java @@ -131,6 +131,82 @@ public class ChatNotificationsPluginTest verify(messageNode).setValue("foo test. bar"); } + @Test + public void testColor() + { + when(config.highlightWordsString()).thenReturn("you. It"); + + String message = "Your dodgy necklace protects you. It has 1 charge left."; + MessageNode messageNode = mock(MessageNode.class); + when(messageNode.getValue()).thenReturn(message); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setMessageNode(messageNode); + + chatNotificationsPlugin.startUp(); // load highlight config + chatNotificationsPlugin.onChatMessage(chatMessage); + + verify(messageNode).setValue("Your dodgy necklace protects you. It has 1 charge left."); + } + + @Test + public void testPreceedingColor() + { + when(config.highlightWordsString()).thenReturn("you. It"); + + String message = "Your dodgy necklace protects you. It has 1 charge left."; + MessageNode messageNode = mock(MessageNode.class); + when(messageNode.getValue()).thenReturn(message); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setMessageNode(messageNode); + + chatNotificationsPlugin.startUp(); // load highlight config + chatNotificationsPlugin.onChatMessage(chatMessage); + + verify(messageNode).setValue("Your dodgy necklace protects you. It has 1 charge left."); + } + + @Test + public void testEmoji() + { + when(config.highlightWordsString()).thenReturn("test"); + + String message = "emoji test "; + MessageNode messageNode = mock(MessageNode.class); + when(messageNode.getValue()).thenReturn(message); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setMessageNode(messageNode); + + chatNotificationsPlugin.startUp(); // load highlight config + chatNotificationsPlugin.onChatMessage(chatMessage); + + verify(messageNode).setValue("emoji test "); + } + + @Test + public void testNonMatchedColors() + { + when(config.highlightWordsString()).thenReturn("test"); + + String message = "color test "; + MessageNode messageNode = mock(MessageNode.class); + when(messageNode.getValue()).thenReturn(message); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setMessageNode(messageNode); + + chatNotificationsPlugin.startUp(); // load highlight config + chatNotificationsPlugin.onChatMessage(chatMessage); + + verify(messageNode).setValue("color test "); + } + @Test public void highlightListTest() { @@ -144,4 +220,10 @@ public class ChatNotificationsPluginTest assertEquals("a", iterator.next()); assertEquals("test", iterator.next()); } + + @Test + public void testStripColor() + { + assertEquals("you. It", ChatNotificationsPlugin.stripColor("you. It")); + } } \ No newline at end of file