mirror of
https://github.com/ppy/osu
synced 2024-12-15 11:25:29 +00:00
Merge pull request #5979 from bdach/brace-escaping-in-links
Fix bracket and parenthesis handling in existing link formats Co-authored-by: Dan Balasescu <smoogipoo@smgi.me>
This commit is contained in:
commit
3b650ac646
@ -119,6 +119,53 @@ namespace osu.Game.Tests.Chat
|
||||
Assert.AreEqual(11, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOldFormatLinkWithBalancedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (tricky (one))[https://osu.ppy.sh]!" });
|
||||
|
||||
Assert.AreEqual("This is a tricky (one)!", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(12, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOldFormatLinkWithEscapedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is (another loose bracket \\))[https://osu.ppy.sh]." });
|
||||
|
||||
Assert.AreEqual("This is another loose bracket ).", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(8, result.Links[0].Index);
|
||||
Assert.AreEqual(23, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOldFormatWithBackslashes()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This link (should end with a backslash \\)[https://osu.ppy.sh]." });
|
||||
Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(29, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOldFormatLinkWithEscapedAndBalancedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a (\\)super\\(\\( tricky (one))[https://osu.ppy.sh]!" });
|
||||
|
||||
Assert.AreEqual("This is a )super(( tricky (one)!", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(21, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewFormatLink()
|
||||
{
|
||||
@ -131,6 +178,42 @@ namespace osu.Game.Tests.Chat
|
||||
Assert.AreEqual(11, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewFormatLinkWithEscapedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh nasty link with escaped brackets: \\] and \\[]" });
|
||||
|
||||
Assert.AreEqual("This is a nasty link with escaped brackets: ] and [", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(41, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewFormatLinkWithBackslashesInside()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh link \\ with \\ backslashes \\]" });
|
||||
|
||||
Assert.AreEqual("This is a link \\ with \\ backslashes \\", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(27, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNewFormatLinkWithEscapedAndBalancedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [https://osu.ppy.sh [link [with \\] too many brackets \\[ ]]]" });
|
||||
|
||||
Assert.AreEqual("This is a [link [with ] too many brackets [ ]]", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(36, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkdownFormatLink()
|
||||
{
|
||||
@ -143,6 +226,53 @@ namespace osu.Game.Tests.Chat
|
||||
Assert.AreEqual(11, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkdownFormatLinkWithBalancedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [tricky [one]](https://osu.ppy.sh)!" });
|
||||
|
||||
Assert.AreEqual("This is a tricky [one]!", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(12, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkdownFormatLinkWithEscapedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is [another loose bracket \\]](https://osu.ppy.sh)." });
|
||||
|
||||
Assert.AreEqual("This is another loose bracket ].", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(8, result.Links[0].Index);
|
||||
Assert.AreEqual(23, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkdownFormatWithBackslashes()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This link [should end with a backslash \\](https://osu.ppy.sh)." });
|
||||
Assert.AreEqual("This link should end with a backslash \\.", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(29, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkdownFormatLinkWithEscapedAndBalancedBrackets()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a [\\]super\\[\\[ tricky [one]](https://osu.ppy.sh)!" });
|
||||
|
||||
Assert.AreEqual("This is a ]super[[ tricky [one]!", result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
|
||||
Assert.AreEqual(10, result.Links[0].Index);
|
||||
Assert.AreEqual(21, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChannelLink()
|
||||
{
|
||||
|
@ -127,6 +127,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
addMessageWithChecks("is now playing [https://osu.ppy.sh/b/252238 IMAGE -MATERIAL- <Version 0>]", 1, true, expectedActions: LinkAction.OpenBeatmap);
|
||||
addMessageWithChecks("Let's (try)[https://osu.ppy.sh/home] [https://osu.ppy.sh/b/252238 multiple links] https://osu.ppy.sh/home", 3,
|
||||
expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External });
|
||||
addMessageWithChecks("[https://osu.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External);
|
||||
addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://osu.ppy.sh/home)", 1, expectedActions: LinkAction.External);
|
||||
addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://osu.ppy.sh/home] and [[also a rogue wiki link]]", 2, expectedActions: new[] { LinkAction.External, LinkAction.External });
|
||||
// note that there's 0 links here (they get removed if a channel is not found)
|
||||
addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present).");
|
||||
addMessageWithChecks("I am important!", 0, false, true);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
@ -10,16 +11,16 @@ namespace osu.Game.Online.Chat
|
||||
public static class MessageFormatter
|
||||
{
|
||||
// [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points)
|
||||
private static readonly Regex wiki_regex = new Regex(@"\[\[([^\]]+)\]\]");
|
||||
private static readonly Regex wiki_regex = new Regex(@"\[\[(?<text>[^\]]+)\]\]");
|
||||
|
||||
// (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234)
|
||||
private static readonly Regex old_link_regex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]");
|
||||
private static readonly Regex old_link_regex = new Regex(@"\((?<text>(((?<=\\)[\(\)])|[^\(\)])*(((?<open>\()(((?<=\\)[\(\)])|[^\(\)])*)+((?<close-open>\))(((?<=\\)[\(\)])|[^\(\)])*)+)*(?(open)(?!)))\)\[(?<url>[a-z]+://[^ ]+)\]");
|
||||
|
||||
// [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234)
|
||||
private static readonly Regex new_link_regex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?<open>\[)[^\[\]]*)+((?<close-open>\])[^\[\]]*)+)*(?(open)(?!)))\]");
|
||||
private static readonly Regex new_link_regex = new Regex(@"\[(?<url>[a-z]+://[^ ]+) (?<text>(((?<=\\)[\[\]])|[^\[\]])*(((?<open>\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?<close-open>\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]");
|
||||
|
||||
// [test](https://osu.ppy.sh/b/1234) -> test (https://osu.ppy.sh/b/1234) aka correct markdown format
|
||||
private static readonly Regex markdown_link_regex = new Regex(@"\[([^\]]*)\]\(([a-z]+://[^ ]+)\)");
|
||||
private static readonly Regex markdown_link_regex = new Regex(@"\[(?<text>(((?<=\\)[\[\]])|[^\[\]])*(((?<open>\[)(((?<=\\)[\[\]])|[^\[\]])*)+((?<close-open>\])(((?<=\\)[\[\]])|[^\[\]])*)+)*(?(open)(?!)))\]\((?<url>[a-z]+://[^ ]+)\)");
|
||||
|
||||
// advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used
|
||||
// This is in the format (<required>, [optional]):
|
||||
@ -48,7 +49,7 @@ namespace osu.Game.Online.Chat
|
||||
// Unicode emojis
|
||||
private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])");
|
||||
|
||||
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null)
|
||||
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null)
|
||||
{
|
||||
int captureOffset = 0;
|
||||
|
||||
@ -58,16 +59,20 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
var displayText = string.Format(display,
|
||||
m.Groups[0],
|
||||
m.Groups.Count > 1 ? m.Groups[1].Value : "",
|
||||
m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
|
||||
m.Groups["text"].Value,
|
||||
m.Groups["url"].Value).Trim();
|
||||
|
||||
var linkText = string.Format(link,
|
||||
m.Groups[0],
|
||||
m.Groups.Count > 1 ? m.Groups[1].Value : "",
|
||||
m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
|
||||
m.Groups["text"].Value,
|
||||
m.Groups["url"].Value).Trim();
|
||||
|
||||
if (displayText.Length == 0 || linkText.Length == 0) continue;
|
||||
|
||||
// Remove backslash escapes in front of the characters provided in escapeChars
|
||||
if (escapeChars != null)
|
||||
displayText = escapeChars.Aggregate(displayText, (current, c) => current.Replace($"\\{c}", c.ToString()));
|
||||
|
||||
// Check for encapsulated links
|
||||
if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null)
|
||||
{
|
||||
@ -183,13 +188,13 @@ namespace osu.Game.Online.Chat
|
||||
var result = new MessageFormatterResult(toFormat);
|
||||
|
||||
// handle the [link display] format
|
||||
handleMatches(new_link_regex, "{2}", "{1}", result, startIndex);
|
||||
handleMatches(new_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' });
|
||||
|
||||
// handle the standard markdown []() format
|
||||
handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex);
|
||||
handleMatches(markdown_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '[', ']' });
|
||||
|
||||
// handle the ()[] link format
|
||||
handleMatches(old_link_regex, "{1}", "{2}", result, startIndex);
|
||||
handleMatches(old_link_regex, "{1}", "{2}", result, startIndex, escapeChars: new[] { '(', ')' });
|
||||
|
||||
// handle wiki links
|
||||
handleMatches(wiki_regex, "{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex);
|
||||
|
Loading…
Reference in New Issue
Block a user