diff --git a/GitUI/Editor/RichTextBoxXhtmlSupportExtension.cs b/GitUI/Editor/RichTextBoxXhtmlSupportExtension.cs index 89dcb7b4ceb..3574cd6d850 100644 --- a/GitUI/Editor/RichTextBoxXhtmlSupportExtension.cs +++ b/GitUI/Editor/RichTextBoxXhtmlSupportExtension.cs @@ -15,6 +15,8 @@ namespace GitUI.Editor.RichTextBoxExtension { internal static class RichTextBoxXhtmlSupportExtension { + private const string LinkSeparator = "|||"; + /// /// Maintains performance while updating. /// @@ -1143,10 +1145,9 @@ public static string GetPlainText(this RichTextBox rtb, int from, int to) ++to; } - if (to < text.Length && text[to] == '#') + if (to < text.Length && DoesMatchLinkSeparatorPattern(text, to)) { - ++to; - from = to; + from = to + LinkSeparator.Length; while (to < text.Length && !char.IsWhiteSpace(text[to]) && text[to] != ',') { ++to; @@ -1156,7 +1157,7 @@ public static string GetPlainText(this RichTextBox rtb, int from, int to) // prior to net47 links were created via hidden text, and had the following format: "text#link" // extract the link portion only string linkOldFormat = text.Substring(from, to - from); - return linkOldFormat.Substring(linkOldFormat.IndexOf("#") + 1); + return linkOldFormat.Substring(linkOldFormat.IndexOf(LinkSeparator) + LinkSeparator.Length); } catch { @@ -1173,6 +1174,19 @@ public static string GetPlainText(this RichTextBox rtb, int from, int to) } } + private static bool DoesMatchLinkSeparatorPattern(string text, int index) + { + for (int ls = 0; ls < LinkSeparator.Length && index < text.Length; ls++, index++) + { + if (text[index] != LinkSeparator[ls]) + { + return false; + } + } + + return true; + } + /// /// Returns input text with characters disallowed by XML spec (e.g. most control codes in 0x00-0x20 range) /// replaced with equivalent character references or question marks if there is an unrecoverable error. @@ -1519,7 +1533,7 @@ private static void ProcessEndElement(XmlReader reader, RTFCurrentState cs, Rich { string head = rtfText.Substring(0, idx); string tail = rtfText.Substring(idx); - RtbSetSelectedRtf(rtb, head + @"\v #" + cs.hyperlink + @"\v0" + tail); + RtbSetSelectedRtf(rtb, head + @"\v " + LinkSeparator + cs.hyperlink + @"\v0" + tail); length = rtb.TextLength - cs.hyperlinkStart; } } diff --git a/UnitTests/GitUI.Tests/Editor/RichTextBoxXhtmlSupportExtensionTests.cs b/UnitTests/GitUI.Tests/Editor/RichTextBoxXhtmlSupportExtensionTests.cs index 5af7f3535b5..a32b027fa00 100644 --- a/UnitTests/GitUI.Tests/Editor/RichTextBoxXhtmlSupportExtensionTests.cs +++ b/UnitTests/GitUI.Tests/Editor/RichTextBoxXhtmlSupportExtensionTests.cs @@ -15,6 +15,7 @@ public class RichTextBoxXhtmlSupportExtensionTests private const string _defaultLinkUri = "https://uri.org"; private const string _defaultPrefix = "pre "; private const string _defaultSuffix = " suf"; + private const string _linkSeparator = "|||"; private RichTextBox _rtb; private ILinkFactory _linkFactory; @@ -86,12 +87,35 @@ public void GetLink_should_return_null_if_left_of_link() public void GetLink_should_return_null_if_right_of_link() { SetupDefaultLink(); - for (int index = _defaultPrefix.Length + _defaultLinkText.Length + _defaultLinkUri.Length + 1; index < _rtb.Text.Length; ++index) + for (int index = _defaultPrefix.Length + _defaultLinkText.Length + _defaultLinkUri.Length + _linkSeparator.Length; index < _rtb.Text.Length; ++index) { _rtb.GetLink(index).Should().BeNull(); } } + [Test] + public void GetLink_should_return_uri_if_text_contains_hash() + { + SetupLink(prefix: string.Empty, linkText: "#hash", uri: _defaultLinkUri, suffix: string.Empty); + _rtb.GetLink(0).Should().Be(_defaultLinkUri); + } + + [Test] + public void GetLink_should_return_uri_if_uri_contains_hash() + { + string uri = _defaultLinkUri + "#hash"; + SetupLink(prefix: string.Empty, linkText: _defaultLinkText, uri: uri, suffix: string.Empty); + _rtb.GetLink(0).Should().Be(uri); + } + + [Test] + public void GetLink_should_return_uri_if_text_and_uri_contain_hash() + { + string uri = _defaultLinkUri + "#hash"; + SetupLink(prefix: string.Empty, linkText: "#text", uri: uri, suffix: string.Empty); + _rtb.GetLink(0).Should().Be(uri); + } + [Test] public void GetLink_should_return_uri_if_at_link() {