/// <summary> /// Highlights elements in the specified text. /// /// The highlighter tries to find matches of the patterns specified by the Patterns property in the provided text. /// For each match a IHighlighterToken instance is returned and the color is used as defined in the matched pattern /// definition. Except for multi line comments an keywords/identifiers the Patterns list also defines a token type /// precedence, i.e. a highlighter token instance is always returned for the first token type matched by the regex, /// even if mutliple token types are matched simultanously. /// </summary> /// <param name="text">Text, which should be highlighted.</param> /// <param name="activeBlock">Active multi line block.</param> /// <returns>Enumeration of IHighlighterToken instances.</returns> public IEnumerable <Interfaces.IHighlighterToken> Highlight(string text, IHighlighterToken activeBlock = null) { if (text != null) { bool activeBlockIsComment = activeBlock != null && activeBlock.Type.Equals("COMMENT", StringComparison.OrdinalIgnoreCase); bool insideComment = activeBlock != null && activeBlock.Type.Equals("COMMENT", StringComparison.OrdinalIgnoreCase); int commentStart = insideComment ? 0 : -1; int commentEnd = -1; uint commentColor = insideComment ? activeBlock.Color : 0; var matches = mPatternRegEx.Matches(text); // Perform regex matching foreach (System.Text.RegularExpressions.Match match in matches) { // Find first group, containing a match for (int i = 1; i <= match.Groups.Count; ++i) { var currentGroup = match.Groups[i]; if (currentGroup.Success) { string tokenType = mPatternRegEx.GroupNameFromNumber(i); // Not in multi-line comment? if (!(insideComment)) { // Special treatment for identifiers and keywords... if (tokenType.Equals("IDENTIFIER", StringComparison.OrdinalIgnoreCase) && mKeywordRegEx != null) { string identifier = currentGroup.Value; var keywordMatch = mKeywordRegEx.Match(identifier); // Keywords have precedence over identifiers, but identifiers have precendence over keywords, // if the keyword is only contained in the identifier. if (keywordMatch.Success && keywordMatch.Length == identifier.Length) { // Determine keyword token type for (int g = 1; g <= keywordMatch.Groups.Count; ++g) { var currentKeywordGroup = keywordMatch.Groups[g]; if (currentKeywordGroup.Success) { tokenType = mKeywordRegEx.GroupNameFromNumber(g); break; } } yield return(new Interfaces.HighlighterToken(mTokenTypeToPattern[tokenType].FirstOrDefault().Color, tokenType, currentGroup.Index, currentGroup.Index + currentGroup.Length)); } } // Multiline comment handling... if (tokenType.Equals("COMMENT_START", StringComparison.OrdinalIgnoreCase) || tokenType.Equals("CommentStart", StringComparison.OrdinalIgnoreCase)) { insideComment = true; commentStart = currentGroup.Index; commentColor = mTokenTypeToPattern[tokenType].FirstOrDefault().Color; } else // Single line comment handling... if (tokenType.Equals("LINE_COMMENT", StringComparison.OrdinalIgnoreCase) || tokenType.Equals("LineComment", StringComparison.OrdinalIgnoreCase)) { commentColor = mTokenTypeToPattern[tokenType].FirstOrDefault().Color; var tagColor = mTokenTypeToPattern["TAG"].FirstOrDefault().Color; // Handle tags... if (mTagRegEx != null) { commentStart = currentGroup.Index; string lineComment = currentGroup.Value; var tags = mTagRegEx.Matches(lineComment); if (tags.Count > 0) { foreach (System.Text.RegularExpressions.Match tag in tags) { commentEnd = tag.Index + currentGroup.Index; int tagStart = tag.Index + currentGroup.Index; int tagEnd = tagStart + tag.Length; if (commentEnd > commentStart) { yield return(new Interfaces.HighlighterToken(commentColor, tokenType, commentStart, commentEnd)); } if (tagEnd > tagStart) { yield return(new Interfaces.HighlighterToken(tagColor, "TAG", tagStart, tagEnd)); } commentStart = tagEnd; } commentEnd = currentGroup.Index + currentGroup.Length; if (commentEnd > commentStart) { yield return(new Interfaces.HighlighterToken(commentColor, tokenType, commentStart, commentEnd)); } } else { yield return(new Interfaces.HighlighterToken(mTokenTypeToPattern[tokenType].FirstOrDefault().Color, tokenType, currentGroup.Index, currentGroup.Index + currentGroup.Length)); } } else { yield return(new Interfaces.HighlighterToken(mTokenTypeToPattern[tokenType].FirstOrDefault().Color, tokenType, currentGroup.Index, currentGroup.Index + currentGroup.Length)); } } else // Normal match... { yield return(new Interfaces.HighlighterToken(mTokenTypeToPattern[tokenType].FirstOrDefault().Color, tokenType, currentGroup.Index, currentGroup.Index + currentGroup.Length)); } } else { // Handle tag if (tokenType.Equals("TAG", StringComparison.OrdinalIgnoreCase)) { commentEnd = currentGroup.Index; yield return(new Interfaces.HighlighterToken(commentColor, "COMMENT", commentStart, commentEnd)); yield return(new Interfaces.HighlighterToken(mTokenTypeToPattern[tokenType].FirstOrDefault().Color, tokenType, currentGroup.Index, currentGroup.Index + currentGroup.Length)); commentStart = currentGroup.Index + currentGroup.Length; insideComment = true; } // Multiline comment handling... if (tokenType.Equals("COMMENT_END", StringComparison.OrdinalIgnoreCase) || tokenType.Equals("CommentEnd", StringComparison.OrdinalIgnoreCase)) { insideComment = false; commentEnd = currentGroup.Index + currentGroup.Length; yield return(new Interfaces.HighlighterToken(commentColor, "COMMENT", commentStart, commentEnd)); } } break; } } } // Emit comment token if not closed if (insideComment && commentStart >= 0) { yield return(new Interfaces.HighlighterToken(commentColor, "COMMENT", commentStart, text.Length)); } } else { yield break; } }
/// <summary> /// Highlights elements in the text to which access is provided through the text reader. /// </summary> /// <param name="inputReader">Text reader, providing access to the text, which should be highlighted.</param> /// <param name="activeBlock">Active multi line block.</param> /// <returns>Enumeration of IHighlighterToken instances.</returns> public IEnumerable <Interfaces.IHighlighterToken> Highlight(System.IO.TextReader inputReader, IHighlighterToken activeBlock = null) { if (inputReader != null) { string text = inputReader.ReadToEnd(); return(Highlight(text, activeBlock)); } else { return(new Interfaces.HighlighterToken[] { }); } }