public static LinkFormatterResult Format(string input, int startIndex = 0, int space = 3)
        {
            LinkFormatterResult result = new LinkFormatterResult(input);

            // handle the [link display] format
            handleMatches(newFormatLink, "{2}", "{1}", result, startIndex);

            // handle the ()[] link format
            handleMatches(oldFormatLink, "{1}", "{2}", result, startIndex);

            // handle wiki links
            handleMatches(regexWiki, "wiki:{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex);

            // handle bare links
            handleAdvanced(advancedLink, result, startIndex);

            // handle editor times
            handleMatches(timeMatch, "{0}", "osu://edit/{0}", result, startIndex);

            // handle channels
            handleMatches(channelMatch, "{0}", "osu://chan/{0}", result, startIndex);

            string empty = "";

            while (space-- > 0)
            {
                empty += "\0";
            }

            handleMatches(emoji, empty, "{0}", result, startIndex); // 3 space,handleMatches will trim all empty char except \0
            //result.Text = result.Text.Replace('\0', ' ');
            return(result);
        }
        private static void handleAdvanced(Regex against, LinkFormatterResult result, int startIndex = 0)
        {
            foreach (Match m in against.Matches(result.Text, startIndex))
            {
                int    index       = m.Index;
                string prefix      = m.Groups["paren"].Value;
                string link        = m.Groups["link"].Value;
                int    indexLength = link.Length;

                if (!String.IsNullOrEmpty(prefix))
                {
                    index += prefix.Length;
                    if (link.EndsWith(")"))
                    {
                        indexLength = indexLength - 1;
                        link        = link.Remove(link.Length - 1);
                    }
                }

                result.Links.Add(new Link(link, index, indexLength));
            }
        }
        private static void handleMatches(Regex against, string display, string link, LinkFormatterResult result, int startIndex = 0)
        {
            int captureOffset = 0;

            foreach (Match m in against.Matches(result.Text, startIndex))
            {
                int index = m.Index - captureOffset;

                string displayText = string.Format(display,
                                                   m.Groups[0],
                                                   m.Groups.Count > 1 ? m.Groups[1].Value : "",
                                                   m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();

                string linkText = string.Format(link,
                                                m.Groups[0],
                                                m.Groups.Count > 1 ? m.Groups[1].Value : "",
                                                m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();

                if (displayText.Length == 0 || linkText.Length == 0)
                {
                    continue;
                }

                //ensure we don't have 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)
                {
                    result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText);

                    //since we just changed the line display text, offset any already processed links.
                    result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0);

                    result.Links.Add(new Link(linkText, index, displayText.Length));

                    //adjust the offset for processing the current matches group.
                    captureOffset += (m.Length - displayText.Length);
                }
            }
        }