public Regex GetCachedRegex(CommentToken token) { // Cache the compiled Regex instances so we can reuse them even across different languages and file extensions. // This is an important performance optimization for common delimiters like "//" and "/* */". string regexCacheKey = this.id + ' ' + token.Text + ' ' + (token.IsCaseSensitive ? '+' : '-'); Regex result = RegexCache.GetOrAdd(regexCacheKey, key => this.CreateRegex(token)); return(result); }
private Regex CreateRegex(CommentToken token) { StringBuilder sb = new StringBuilder(); if (!string.IsNullOrEmpty(this.begin)) { // Allow optional whitespace after the comment begins. sb.Append(Regex.Escape(this.begin)).Append(@"[ \t]*"); } else { // If there's no begin delimiter (e.g., in a PlainText file), then start from // the beginning of the line or require a non-word character before the token. // https://msdn.microsoft.com/en-us/library/20bw873z.aspx#WordCharacter sb.Append(@"(^|[^\w])"); } // Begin a named group to get the comment text. sb.Append("(?<" + RegexCommentGroupName + ">"); if (token.IsCaseSensitive) { sb.Append("(?-i:"); } sb.Append(Regex.Escape(token.Text)); if (token.IsCaseSensitive) { sb.Append(")"); } // Support an optional colon, space, or tab followed by any sequence of characters. // But we also have to support simple comments like "-- TODO" or "(* TODO *)". const string SeparatorPattern = @"[: \t]+.*"; if (string.IsNullOrEmpty(this.end)) { // Close the optional separator and the named group then match to the end of the string. sb.Append("(").Append(SeparatorPattern).Append(")?)$"); } else { // Support an optional separator pattern ending with the lazy match token (?) so .* won't match // to the end of the string if the end delimiter is matched first. Then close the optional separator // and the named group and then match to the end of the string or to the end delimiter. // http://stackoverflow.com/a/6738624/1882616 and http://www.regular-expressions.info/repeat.html sb.Append(@"(").Append(SeparatorPattern).Append("?)?)($|").Append(Regex.Escape(this.end)).Append(")"); } // Ignore case overall because even in case-sensitive languages, most tokens need to be match case-insensitively. // Also, in some languages the case for comment delimiters needs to be ignored (e.g., REM in .bat files). // The options for Compiled and CultureInvariant are just to make it match as fast as possible. string regexPattern = sb.ToString(); Regex result = new Regex(regexPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant); return(result); }
public IEnumerable <Regex> GetTokenRegexes(CommentToken token, bool preferSingleLineOnly) { IEnumerable <Delimiter> preferredDelimiters = preferSingleLineOnly && this.delimiters.Any(d => d.IsSingleLine) ? this.delimiters.Where(d => d.IsSingleLine) : this.delimiters; foreach (Delimiter delimiter in preferredDelimiters) { yield return(delimiter.GetCachedRegex(token)); } }
public IEnumerable <Regex> GetTokenRegexes(CommentToken token) { IEnumerable <Regex> result = this.GetTokenRegexes(token, false); return(result); }