///<summary>Called on KeyUp from various textBoxes in the program to look for a ?abbrev and attempt to substitute. Substitutes the text if found.</summary> public static string Substitute(string text, QuickPasteType type) { //No need to check RemotingRole; no call to db. List <QuickPasteCat> listQuickPasteCatsForType = QuickPasteCats.GetCategoriesForType(type); if (listQuickPasteCatsForType.Count == 0) { return(text); } List <QuickPasteNote> listQuickPasteNotes = QuickPasteNotes.GetForCats(listQuickPasteCatsForType.OrderBy(x => x.ItemOrder).ToList()); for (int i = 0; i < listQuickPasteNotes.Count; i++) { if (listQuickPasteNotes[i].Abbreviation == "") { continue; } //We have to replace all $ chars with $$ because Regex.Replace allows "Substitutions" in the replacement parameter. //The replacement parameter specifies the string that is to replace each match in input. replacement can consist of any combination of literal //text and substitutions. For example, the replacement pattern a*${test}b inserts the string "a*" followed by the substring that is matched by //the test capturing group, if any, followed by the string "b". //The * character is not recognized as a metacharacter within a replacement pattern. //See https://msdn.microsoft.com/en-us/library/taz3ak2f(v=vs.110).aspx for more information. string quicknote = listQuickPasteNotes[i].Note.Replace("$", "$$"); //Techs were complaining about quick notes replacing text that was pasted into text boxes (e.g. when a URL happens to have ?... that matches a quick note abbr). //The easiest way to deal with this is to not allow the regular expression to replace strings that have a non-whitespace character before or after the abbr. //The regex of '...(?<!\S)...' is utilizing an optional space via a lookbehind and visa versa with '...(?!\S)...' as a lookahead. var pattern = @"(?<spaceBefore>(?<!\S))\?" + Regex.Escape(listQuickPasteNotes[i].Abbreviation) + @"(?<spaceAfter>(?!\S))"; var replacePattern = "${spaceBefore}" + (quicknote) + "${spaceAfter}"; text = Regex.Replace(text, pattern, replacePattern, RegexOptions.None); } //If we didn't find any matches then return the passed in text return(text); }