public virtual void MarkTokens(IDocument document) { if (Rules.Count == 0) { return; } var lineNumber = 0; while (lineNumber < document.TotalNumberOfLines) { var previousLine = lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null; if (lineNumber >= document.LineSegmentCollection.Count ) // may be, if the last line ends with a delimiter { break; // then the last line is not in the collection :) } currentSpanStack = previousLine != null && previousLine.HighlightSpanStack != null ? previousLine.HighlightSpanStack.Clone() : null; if (currentSpanStack != null) { while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL) { currentSpanStack.Pop(); } if (currentSpanStack.IsEmpty) { currentSpanStack = null; } } currentLine = document.LineSegmentCollection[lineNumber]; if (currentLine.Length == -1) // happens when buffer is empty ! { return; } currentLineNumber = lineNumber; var words = ParseLine(document); // Alex: clear old words if (currentLine.Words != null) { currentLine.Words.Clear(); } currentLine.Words = words; currentLine.HighlightSpanStack = currentSpanStack == null || currentSpanStack.IsEmpty ? null : currentSpanStack; ++lineNumber; } document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea)); document.CommitUpdate(); currentLine = null; }
private List <TextWord> ParseLine(IDocument document) { var words = new List <TextWord>(); HighlightColor markNext = null; currentOffset = 0; currentLength = 0; UpdateSpanStateVariables(); var currentLineLength = currentLine.Length; var currentLineOffset = currentLine.Offset; for (var i = 0; i < currentLineLength; ++i) { var ch = document.GetCharAt(currentLineOffset + i); switch (ch) { case '\n': case '\r': PushCurWord(document, ref markNext, words); ++currentOffset; continue; case ' ': PushCurWord(document, ref markNext, words); if (activeSpan != null && activeSpan.Color.HasBackground) { words.Add(new TextWord.SpaceTextWord(activeSpan.Color)); } else { words.Add(TextWord.Space); } ++currentOffset; continue; case '\t': PushCurWord(document, ref markNext, words); if (activeSpan != null && activeSpan.Color.HasBackground) { words.Add(new TextWord.TabTextWord(activeSpan.Color)); } else { words.Add(TextWord.Tab); } ++currentOffset; continue; } // handle escape characters var escapeCharacter = '\0'; if (activeSpan != null && activeSpan.EscapeCharacter != '\0') { escapeCharacter = activeSpan.EscapeCharacter; } else if (activeRuleSet != null) { escapeCharacter = activeRuleSet.EscapeCharacter; } if (escapeCharacter != '\0' && escapeCharacter == ch) { // we found the escape character if (activeSpan != null && activeSpan.End != null && activeSpan.End.Length == 1 && escapeCharacter == activeSpan.End[0]) { // the escape character is a end-doubling escape character // it may count as escape only when the next character is the escape, too if (i + 1 < currentLineLength) { if (document.GetCharAt(currentLineOffset + i + 1) == escapeCharacter) { currentLength += 2; PushCurWord(document, ref markNext, words); ++i; continue; } } } else { // this is a normal \-style escape ++currentLength; if (i + 1 < currentLineLength) { ++currentLength; } PushCurWord(document, ref markNext, words); ++i; continue; } } // highlight digits if (!inSpan && (char.IsDigit(ch) || ch == '.' && i + 1 < currentLineLength && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) && currentLength == 0) { var ishex = false; var isfloatingpoint = false; if (ch == '0' && i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'X') { // hex digits const string hex = "0123456789ABCDEF"; ++currentLength; ++i; // skip 'x' ++currentLength; ishex = true; while (i + 1 < currentLineLength && hex.IndexOf(char.ToUpper(document.GetCharAt(currentLineOffset + i + 1))) != -1) { ++i; ++currentLength; } } else { ++currentLength; while (i + 1 < currentLineLength && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) { ++i; ++currentLength; } } if (!ishex && i + 1 < currentLineLength && document.GetCharAt(currentLineOffset + i + 1) == '.') { isfloatingpoint = true; ++i; ++currentLength; while (i + 1 < currentLineLength && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) { ++i; ++currentLength; } } if (i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'E') { isfloatingpoint = true; ++i; ++currentLength; if (i + 1 < currentLineLength && (document.GetCharAt(currentLineOffset + i + 1) == '+' || document.GetCharAt(currentLine.Offset + i + 1) == '-')) { ++i; ++currentLength; } while (i + 1 < currentLine.Length && char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) { ++i; ++currentLength; } } if (i + 1 < currentLine.Length) { var nextch = char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)); if (nextch == 'F' || nextch == 'M' || nextch == 'D') { isfloatingpoint = true; ++i; ++currentLength; } } if (!isfloatingpoint) { var isunsigned = false; if (i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') { ++i; ++currentLength; isunsigned = true; } if (i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'L') { ++i; ++currentLength; if (!isunsigned && i + 1 < currentLineLength && char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') { ++i; ++currentLength; } } } words.Add(new TextWord(document, currentLine, currentOffset, currentLength, DigitColor, false)); currentOffset += currentLength; currentLength = 0; continue; } // Check for SPAN ENDs if (inSpan) { if (activeSpan.End != null && activeSpan.End.Length > 0) { if (MatchExpr(currentLine, activeSpan.End, i, document, activeSpan.IgnoreCase)) { PushCurWord(document, ref markNext, words); var regex = GetRegString(currentLine, activeSpan.End, i, document); currentLength += regex.Length; words.Add(new TextWord(document, currentLine, currentOffset, currentLength, activeSpan.EndColor, false)); currentOffset += currentLength; currentLength = 0; i += regex.Length - 1; currentSpanStack.Pop(); UpdateSpanStateVariables(); continue; } } } // check for SPAN BEGIN if (activeRuleSet != null) { Span mySpan = null; foreach (Span span in activeRuleSet.Spans) { if (span.IsBeginSingleWord && currentLength != 0) { continue; } if (span.IsBeginStartOfLine.HasValue && span.IsBeginStartOfLine.Value != (currentLength == 0 && words.TrueForAll( delegate(TextWord textWord) { return(textWord.Type != TextWordType.Word); }))) { continue; } if (!MatchExpr(currentLine, span.Begin, i, document, activeRuleSet.IgnoreCase)) { continue; } mySpan = span; break; } if (mySpan != null) { PushCurWord(document, ref markNext, words); var regex = GetRegString(currentLine, mySpan.Begin, i, document); if (!OverrideSpan(regex, document, words, mySpan, ref i)) { currentLength += regex.Length; words.Add(new TextWord(document, currentLine, currentOffset, currentLength, mySpan.BeginColor, false)); currentOffset += currentLength; currentLength = 0; i += regex.Length - 1; if (currentSpanStack == null) { currentSpanStack = new SpanStack(); } currentSpanStack.Push(mySpan); mySpan.IgnoreCase = activeRuleSet.IgnoreCase; UpdateSpanStateVariables(); } continue; } } // check if the char is a delimiter if (activeRuleSet != null && ch < 256 && activeRuleSet.Delimiters[ch]) { PushCurWord(document, ref markNext, words); if (currentOffset + currentLength + 1 < currentLine.Length) { ++currentLength; PushCurWord(document, ref markNext, words); continue; } } ++currentLength; } PushCurWord(document, ref markNext, words); OnParsedLine(document, currentLine, words); return(words); }
private bool MarkTokensInLine(IDocument document, int lineNumber, ref bool spanChanged) { currentLineNumber = lineNumber; var processNextLine = false; var previousLine = lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null; currentSpanStack = previousLine != null && previousLine.HighlightSpanStack != null ? previousLine.HighlightSpanStack.Clone() : null; if (currentSpanStack != null) { while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL) { currentSpanStack.Pop(); } if (currentSpanStack.IsEmpty) { currentSpanStack = null; } } currentLine = document.LineSegmentCollection[lineNumber]; if (currentLine.Length == -1) // happens when buffer is empty ! { return(false); } var words = ParseLine(document); if (currentSpanStack != null && currentSpanStack.IsEmpty) { currentSpanStack = null; } // Check if the span state has changed, if so we must re-render the next line // This check may seem utterly complicated but I didn't want to introduce any function calls // or allocations here for perf reasons. if (currentLine.HighlightSpanStack != currentSpanStack) { if (currentLine.HighlightSpanStack == null) { processNextLine = false; foreach (var sp in currentSpanStack) { if (!sp.StopEOL) { spanChanged = true; processNextLine = true; break; } } } else if (currentSpanStack == null) { processNextLine = false; foreach (var sp in currentLine.HighlightSpanStack) { if (!sp.StopEOL) { spanChanged = true; processNextLine = true; break; } } } else { var e1 = currentSpanStack.GetEnumerator(); var e2 = currentLine.HighlightSpanStack.GetEnumerator(); var done = false; while (!done) { var blockSpanIn1 = false; while (e1.MoveNext()) { if (!e1.Current.StopEOL) { blockSpanIn1 = true; break; } } var blockSpanIn2 = false; while (e2.MoveNext()) { if (!e2.Current.StopEOL) { blockSpanIn2 = true; break; } } if (blockSpanIn1 || blockSpanIn2) { if (blockSpanIn1 && blockSpanIn2) { if (e1.Current != e2.Current) { done = true; processNextLine = true; spanChanged = true; } } else { spanChanged = true; done = true; processNextLine = true; } } else { done = true; processNextLine = false; } } } } else { processNextLine = false; } //// Alex: remove old words if (currentLine.Words != null) { currentLine.Words.Clear(); } currentLine.Words = words; currentLine.HighlightSpanStack = currentSpanStack != null && !currentSpanStack.IsEmpty ? currentSpanStack : null; return(processNextLine); }