Browse Source

Simplify HighlightingColorizer by supporting only a single TextView per colorizer instance; and by exposing the HighlightingStateChanged callback as an event.

Move the HighlightedLine merging logic into AvalonEdit.
newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
39d91dad79
  1. 9
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs
  2. 126
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs
  3. 37
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs
  4. 110
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs
  5. 64
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs
  6. 29
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs

9
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs

@ -171,6 +171,15 @@ namespace CSharpBinding @@ -171,6 +171,15 @@ namespace CSharpBinding
return null;
}
event HighlightingStateChangedEventHandler IHighlighter.HighlightingStateChanged {
add { }
remove { }
}
void IHighlighter.UpdateHighlightingState(int lineNumber)
{
}
public HighlightedLine HighlightLine(int lineNumber)
{
IDocumentLine documentLine = textEditor.Document.GetLineByNumber(lineNumber);

126
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CustomizableHighlightingColorizer.cs

@ -145,6 +145,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -145,6 +145,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
this.customizations = customizations;
this.highlightingDefinition = highlightingDefinition;
this.baseHighlighter = baseHighlighter;
baseHighlighter.HighlightingStateChanged += highlighter_HighlightingStateChanged;
}
public IDocument Document {
@ -155,6 +156,16 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -155,6 +156,16 @@ namespace ICSharpCode.AvalonEdit.AddIn
get { return highlightingDefinition; }
}
public event HighlightingStateChangedEventHandler HighlightingStateChanged;
public void UpdateHighlightingState(int lineNumber)
{
baseHighlighter.UpdateHighlightingState(lineNumber);
foreach (var h in additionalHighlighters) {
h.UpdateHighlightingState(lineNumber);
}
}
public void AddAdditionalHighlighter(IHighlighter highlighter)
{
if (highlighter == null)
@ -162,11 +173,19 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -162,11 +173,19 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (highlighter.Document != baseHighlighter.Document)
throw new ArgumentException("Additional highlighters must use the same document as the base highlighter");
additionalHighlighters.Add(highlighter);
highlighter.HighlightingStateChanged += highlighter_HighlightingStateChanged;
}
public void RemoveAdditionalHighlighter(IHighlighter highlighter)
{
additionalHighlighters.Remove(highlighter);
highlighter.HighlightingStateChanged -= highlighter_HighlightingStateChanged;
}
void highlighter_HighlightingStateChanged(IHighlighter sender, int lineNumber)
{
if (HighlightingStateChanged != null)
HighlightingStateChanged(this, lineNumber);
}
public IEnumerable<string> GetSpanColorNamesFromLineStart(int lineNumber)
@ -195,7 +214,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -195,7 +214,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
{
HighlightedLine line = baseHighlighter.HighlightLine(lineNumber);
foreach (IHighlighter h in additionalHighlighters) {
MergeHighlighting(line, h.HighlightLine(lineNumber));
line.MergeWith(h.HighlightLine(lineNumber));
}
foreach (HighlightedSection section in line.Sections) {
section.Color = CustomizeColor(section.Color);
@ -203,111 +222,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -203,111 +222,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
return line;
}
#region MergeHighlighting
/// <summary>
/// Merges the highlighting sections from additionalLine into line.
/// </summary>
void MergeHighlighting(HighlightedLine line, HighlightedLine additionalLine)
{
if (additionalLine == null)
return;
ValidateInvariants(line);
ValidateInvariants(additionalLine);
int pos = 0;
Stack<int> activeSectionEndOffsets = new Stack<int>();
int lineEndOffset = line.DocumentLine.EndOffset;
activeSectionEndOffsets.Push(lineEndOffset);
foreach (HighlightedSection newSection in additionalLine.Sections) {
int newSectionStart = newSection.Offset;
// Track the existing sections using the stack, up to the point where
// we need to insert the first part of the newSection
while (pos < line.Sections.Count) {
HighlightedSection s = line.Sections[pos];
if (newSection.Offset < s.Offset)
break;
while (s.Offset > activeSectionEndOffsets.Peek()) {
activeSectionEndOffsets.Pop();
}
activeSectionEndOffsets.Push(s.Offset + s.Length);
pos++;
}
// Now insert the new section
// Create a copy of the stack so that we can track the sections we traverse
// during the insertion process:
Stack<int> insertionStack = new Stack<int>(activeSectionEndOffsets.Reverse());
// The stack enumerator reverses the order of the elements, so we call Reverse() to restore
// the original order.
int i;
for (i = pos; i < line.Sections.Count; i++) {
HighlightedSection s = line.Sections[i];
if (newSection.Offset + newSection.Length <= s.Offset)
break;
// Insert a segment in front of s:
Insert(line.Sections, ref i, ref newSectionStart, s.Offset, newSection.Color, insertionStack);
while (s.Offset > insertionStack.Peek()) {
insertionStack.Pop();
}
insertionStack.Push(s.Offset + s.Length);
}
Insert(line.Sections, ref i, ref newSectionStart, newSection.Offset + newSection.Length, newSection.Color, insertionStack);
}
ValidateInvariants(line);
}
void Insert(IList<HighlightedSection> sections, ref int pos, ref int newSectionStart, int insertionEndPos, HighlightingColor color, Stack<int> insertionStack)
{
if (newSectionStart >= insertionEndPos) {
// nothing to insert here
return;
}
while (insertionStack.Peek() <= newSectionStart) {
insertionStack.Pop();
}
while (insertionStack.Peek() < insertionEndPos) {
int end = insertionStack.Pop();
// insert the portion from newSectionStart to end
sections.Insert(pos++, new HighlightedSection {
Offset = newSectionStart,
Length = end - newSectionStart,
Color = color
});
newSectionStart = end;
}
sections.Insert(pos++, new HighlightedSection {
Offset = newSectionStart,
Length = insertionEndPos - newSectionStart,
Color = color
});
newSectionStart = insertionEndPos;
}
[Conditional("DEBUG")]
void ValidateInvariants(HighlightedLine line)
{
int lineStartOffset = line.DocumentLine.Offset;
int lineEndOffset = line.DocumentLine.EndOffset;
for (int i = 0; i < line.Sections.Count; i++) {
HighlightedSection s1 = line.Sections[i];
if (s1.Offset < lineStartOffset || s1.Length < 0 || s1.Offset + s1.Length > lineEndOffset)
throw new InvalidOperationException("Section is outside line bounds");
for (int j = i + 1; j < line.Sections.Count; j++) {
HighlightedSection s2 = line.Sections[j];
if (s2.Offset >= s1.Offset + s1.Length) {
// s2 is after s1
} else if (s2.Offset >= s1.Offset && s2.Offset + s2.Length <= s1.Offset + s1.Length) {
// s2 is nested within s1
} else {
throw new InvalidOperationException("Sections are overlapping or incorrectly sorted.");
}
}
}
}
#endregion
HighlightingColor CustomizeColor(HighlightingColor color)
{
if (color == null || color.Name == null)

37
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/DocumentHighlighter.cs

@ -147,7 +147,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -147,7 +147,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
CheckIsHighlighting();
isHighlighting = true;
try {
HighlightUpTo(lineNumber);
HighlightUpTo(lineNumber - 1);
IDocumentLine line = document.GetLineByNumber(lineNumber);
highlightedLine = new HighlightedLine(document, line);
HighlightLineAndUpdateTreeList(line, lineNumber);
@ -170,13 +170,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -170,13 +170,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
{
ThrowUtil.CheckInRangeInclusive(lineNumber, "lineNumber", 0, document.LineCount);
if (firstInvalidLine <= lineNumber) {
CheckIsHighlighting();
isHighlighting = true;
try {
HighlightUpTo(lineNumber + 1);
} finally {
isHighlighting = false;
}
UpdateHighlightingState(lineNumber);
}
return storedSpanStacks[lineNumber];
}
@ -194,10 +188,22 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -194,10 +188,22 @@ namespace ICSharpCode.AvalonEdit.Highlighting
}
}
/// <inheritdoc/>
public void UpdateHighlightingState(int lineNumber)
{
CheckIsHighlighting();
isHighlighting = true;
try {
HighlightUpTo(lineNumber);
} finally {
isHighlighting = false;
}
}
void HighlightUpTo(int targetLineNumber)
{
Debug.Assert(highlightedLine == null); // ensure this method is only used for
while (firstInvalidLine < targetLineNumber) {
Debug.Assert(highlightedLine == null); // ensure this method is only outside the actual highlighting logic
while (firstInvalidLine <= targetLineNumber) {
HighlightLineAndUpdateTreeList(document.GetLineByNumber(firstInvalidLine), firstInvalidLine);
}
}
@ -217,7 +223,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -217,7 +223,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
} else {
firstInvalidLine = int.MaxValue;
}
OnHighlightStateChanged(line, lineNumber);
OnHighlightStateChanged(lineNumber);
} else if (firstInvalidLine == lineNumber) {
isValid[lineNumber] = true;
firstInvalidLine = isValid.IndexOf(false);
@ -228,7 +234,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -228,7 +234,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
static bool EqualSpanStacks(SpanStack a, SpanStack b)
{
// We must use value equality between the stacks because TextViewDocumentHighlighter.OnHighlightStateChanged
// We must use value equality between the stacks because HighlightingColorizer.OnHighlightStateChanged
// depends on the fact that equal input state + unchanged line contents produce equal output state.
if (a == b)
return true;
@ -245,14 +251,19 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -245,14 +251,19 @@ namespace ICSharpCode.AvalonEdit.Highlighting
return a.IsEmpty && b.IsEmpty;
}
/// <inheritdoc/>
public event HighlightingStateChangedEventHandler HighlightingStateChanged;
/// <summary>
/// Is called when the highlighting state at the end of the specified line has changed.
/// </summary>
/// <remarks>This callback must not call HighlightLine or InvalidateHighlighting.
/// It may call GetSpanStack, but only for the changed line and lines above.
/// This method must not modify the document.</remarks>
protected virtual void OnHighlightStateChanged(IDocumentLine line, int lineNumber)
protected virtual void OnHighlightStateChanged(int lineNumber)
{
if (HighlightingStateChanged != null)
HighlightingStateChanged(this, lineNumber);
}
#region Highlighting Engine

110
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs

@ -3,9 +3,10 @@ @@ -3,9 +3,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.NRefactory.Editor;
@ -49,7 +50,113 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -49,7 +50,113 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary>
public IList<HighlightedSection> Sections { get; private set; }
[Conditional("DEBUG")]
void ValidateInvariants()
{
var line = this;
int lineStartOffset = line.DocumentLine.Offset;
int lineEndOffset = line.DocumentLine.EndOffset;
for (int i = 0; i < line.Sections.Count; i++) {
HighlightedSection s1 = line.Sections[i];
if (s1.Offset < lineStartOffset || s1.Length < 0 || s1.Offset + s1.Length > lineEndOffset)
throw new InvalidOperationException("Section is outside line bounds");
for (int j = i + 1; j < line.Sections.Count; j++) {
HighlightedSection s2 = line.Sections[j];
if (s2.Offset >= s1.Offset + s1.Length) {
// s2 is after s1
} else if (s2.Offset >= s1.Offset && s2.Offset + s2.Length <= s1.Offset + s1.Length) {
// s2 is nested within s1
} else {
throw new InvalidOperationException("Sections are overlapping or incorrectly sorted.");
}
}
}
}
#region Merge
/// <summary>
/// Merges the additional line into this line.
/// </summary>
public void MergeWith(HighlightedLine additionalLine)
{
if (additionalLine == null)
return;
ValidateInvariants();
additionalLine.ValidateInvariants();
int pos = 0;
Stack<int> activeSectionEndOffsets = new Stack<int>();
int lineEndOffset = this.DocumentLine.EndOffset;
activeSectionEndOffsets.Push(lineEndOffset);
foreach (HighlightedSection newSection in additionalLine.Sections) {
int newSectionStart = newSection.Offset;
// Track the existing sections using the stack, up to the point where
// we need to insert the first part of the newSection
while (pos < this.Sections.Count) {
HighlightedSection s = this.Sections[pos];
if (newSection.Offset < s.Offset)
break;
while (s.Offset > activeSectionEndOffsets.Peek()) {
activeSectionEndOffsets.Pop();
}
activeSectionEndOffsets.Push(s.Offset + s.Length);
pos++;
}
// Now insert the new section
// Create a copy of the stack so that we can track the sections we traverse
// during the insertion process:
Stack<int> insertionStack = new Stack<int>(activeSectionEndOffsets.Reverse());
// The stack enumerator reverses the order of the elements, so we call Reverse() to restore
// the original order.
int i;
for (i = pos; i < this.Sections.Count; i++) {
HighlightedSection s = this.Sections[i];
if (newSection.Offset + newSection.Length <= s.Offset)
break;
// Insert a segment in front of s:
Insert(ref i, ref newSectionStart, s.Offset, newSection.Color, insertionStack);
while (s.Offset > insertionStack.Peek()) {
insertionStack.Pop();
}
insertionStack.Push(s.Offset + s.Length);
}
Insert(ref i, ref newSectionStart, newSection.Offset + newSection.Length, newSection.Color, insertionStack);
}
ValidateInvariants();
}
void Insert(ref int pos, ref int newSectionStart, int insertionEndPos, HighlightingColor color, Stack<int> insertionStack)
{
if (newSectionStart >= insertionEndPos) {
// nothing to insert here
return;
}
while (insertionStack.Peek() <= newSectionStart) {
insertionStack.Pop();
}
while (insertionStack.Peek() < insertionEndPos) {
int end = insertionStack.Pop();
// insert the portion from newSectionStart to end
this.Sections.Insert(pos++, new HighlightedSection {
Offset = newSectionStart,
Length = end - newSectionStart,
Color = color
});
newSectionStart = end;
}
this.Sections.Insert(pos++, new HighlightedSection {
Offset = newSectionStart,
Length = insertionEndPos - newSectionStart,
Color = color
});
newSectionStart = insertionEndPos;
}
#endregion
#region ToHtml
sealed class HtmlElement : IComparable<HtmlElement>
{
internal readonly int Offset;
@ -146,5 +253,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -146,5 +253,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting
{
return "[" + GetType().Name + " " + ToHtml(new HtmlOptions()) + "]";
}
#endregion
}
}

64
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColorizer.cs

@ -18,6 +18,8 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -18,6 +18,8 @@ namespace ICSharpCode.AvalonEdit.Highlighting
public class HighlightingColorizer : DocumentColorizingTransformer
{
readonly HighlightingRuleSet ruleSet;
TextView textView;
IHighlighter highlighter;
/// <summary>
/// Creates a new HighlightingColorizer instance.
@ -43,8 +45,13 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -43,8 +45,13 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary>
protected virtual void DeregisterServices(TextView textView)
{
// remove existing highlighter, if any exists
if (highlighter != null) {
highlighter.HighlightingStateChanged -= OnHighlightStateChanged;
// remove highlighter if it is registered
if (textView.Services.GetService(typeof(IHighlighter)) == highlighter)
textView.Services.RemoveService(typeof(IHighlighter));
}
}
/// <summary>
@ -53,11 +60,16 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -53,11 +60,16 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary>
protected virtual void RegisterServices(TextView textView)
{
TextDocument document = textView.Document;
if (document != null) {
IHighlighter highlighter = CreateHighlighter(textView, document);
if (textView.Document != null) {
highlighter = textView.Document != null ? CreateHighlighter(textView, textView.Document) : null;
if (highlighter != null) {
// add service only if it doesn't already exist
if (textView.Services.GetService(typeof(IHighlighter)) == null) {
textView.Services.AddService(typeof(IHighlighter), highlighter);
}
highlighter.HighlightingStateChanged += OnHighlightStateChanged;
}
}
}
/// <summary>
@ -65,13 +77,17 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -65,13 +77,17 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary>
protected virtual IHighlighter CreateHighlighter(TextView textView, TextDocument document)
{
return new TextViewDocumentHighlighter(this, textView, document, ruleSet);
return new DocumentHighlighter(document, ruleSet);
}
/// <inheritdoc/>
protected override void OnAddToTextView(TextView textView)
{
if (this.textView != null) {
throw new InvalidOperationException("Cannot use a HighlightingColorizer instance in multiple text views. Please create a separate instance for each text view.");
}
base.OnAddToTextView(textView);
this.textView = textView;
textView.DocumentChanged += textView_DocumentChanged;
textView.VisualLineConstructionStarting += textView_VisualLineConstructionStarting;
RegisterServices(textView);
@ -84,18 +100,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -84,18 +100,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
textView.DocumentChanged -= textView_DocumentChanged;
textView.VisualLineConstructionStarting -= textView_VisualLineConstructionStarting;
base.OnRemoveFromTextView(textView);
this.textView = null;
}
void textView_VisualLineConstructionStarting(object sender, VisualLineConstructionStartEventArgs e)
{
IHighlighter highlighter = ((TextView)sender).Services.GetService(typeof(IHighlighter)) as IHighlighter;
if (highlighter != null) {
// Force update of highlighting state up to the position where we start generating visual lines.
// This is necessary in case the document gets modified above the FirstLineInView so that the highlighting state changes.
// We need to detect this case and issue a redraw (through TextViewDocumentHighligher.OnHighlightStateChanged)
// before the visual line construction reuses existing lines that were built using the invalid highlighting state.
lineNumberBeingColorized = e.FirstLineInView.LineNumber - 1;
highlighter.GetColorStack(lineNumberBeingColorized);
highlighter.UpdateHighlightingState(lineNumberBeingColorized);
lineNumberBeingColorized = 0;
}
}
@ -108,14 +124,13 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -108,14 +124,13 @@ namespace ICSharpCode.AvalonEdit.Highlighting
this.lastColorizedLine = null;
base.Colorize(context);
if (this.lastColorizedLine != context.VisualLine.LastDocumentLine) {
IHighlighter highlighter = context.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter;
if (highlighter != null) {
// In some cases, it is possible that we didn't highlight the last document line within the visual line
// (e.g. when the line ends with a fold marker).
// But even if we didn't highlight it, we'll have to update the highlighting state for it so that the
// proof inside TextViewDocumentHighlighter.OnHighlightStateChanged holds.
lineNumberBeingColorized = context.VisualLine.LastDocumentLine.LineNumber;
highlighter.GetColorStack(lineNumberBeingColorized);
highlighter.UpdateHighlightingState(lineNumberBeingColorized);
lineNumberBeingColorized = 0;
}
}
@ -127,7 +142,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -127,7 +142,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <inheritdoc/>
protected override void ColorizeLine(DocumentLine line)
{
IHighlighter highlighter = CurrentContext.TextView.Services.GetService(typeof(IHighlighter)) as IHighlighter;
if (highlighter != null) {
lineNumberBeingColorized = line.LineNumber;
HighlightedLine hl = highlighter.HighlightLine(lineNumberBeingColorized);
@ -181,41 +195,21 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -181,41 +195,21 @@ namespace ICSharpCode.AvalonEdit.Highlighting
}
/// <summary>
/// This class is responsible for telling the TextView to redraw lines when the highlighting state has changed.
/// This method is responsible for telling the TextView to redraw lines when the highlighting state has changed.
/// </summary>
/// <remarks>
/// Creation of a VisualLine triggers the syntax highlighter (which works on-demand), so it says:
/// Hey, the user typed "/*". Don't just recreate that line, but also the next one
/// because my highlighting state (at end of line) changed!
/// </remarks>
sealed class TextViewDocumentHighlighter : DocumentHighlighter
void OnHighlightStateChanged(IHighlighter sender, int lineNumber)
{
readonly HighlightingColorizer colorizer;
readonly TextView textView;
public TextViewDocumentHighlighter(HighlightingColorizer colorizer, TextView textView, TextDocument document, HighlightingRuleSet baseRuleSet)
: base(document, baseRuleSet)
{
Debug.Assert(colorizer != null);
Debug.Assert(textView != null);
this.colorizer = colorizer;
this.textView = textView;
}
protected override void OnHighlightStateChanged(IDocumentLine line, int lineNumber)
{
base.OnHighlightStateChanged(line, lineNumber);
if (colorizer.lineNumberBeingColorized != lineNumber) {
if (lineNumberBeingColorized != lineNumber) {
// Ignore notifications for any line except the one we're interested in.
// This improves the performance as Redraw() can take quite some time when called repeatedly
// while scanning the document (above the visible area) for highlighting changes.
return;
}
if (textView.Document != this.Document) {
// May happen if document on text view was changed but some user code is still using the
// existing IHighlighter instance.
return;
}
// The user may have inserted "/*" into the current line, and so far only that line got redrawn.
// So when the highlighting state is changed, we issue a redraw for the line immediately below.
@ -263,7 +257,8 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -263,7 +257,8 @@ namespace ICSharpCode.AvalonEdit.Highlighting
// so it will always invalidate the next visual line when a folded line is constructed
// and the highlighting stack has changed.
textView.Redraw(line.NextLine, DispatcherPriority.Normal);
if (lineNumber + 1 <= textView.Document.LineCount)
textView.Redraw(textView.Document.GetLineByNumber(lineNumber + 1), DispatcherPriority.Normal);
/*
* Meta-comment: "why does this have to be so complicated?"
@ -278,5 +273,4 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -278,5 +273,4 @@ namespace ICSharpCode.AvalonEdit.Highlighting
*/
}
}
}
}

29
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/IHighlighter.cs

@ -37,5 +37,34 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -37,5 +37,34 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <param name="lineNumber">The line to highlight.</param>
/// <returns>A <see cref="HighlightedLine"/> line object that represents the highlighted sections.</returns>
HighlightedLine HighlightLine(int lineNumber);
/// <summary>
/// Enforces a highlighting state update (triggering the HighlightingStateChanged event if necessary)
/// for all lines up to (and inclusive) the specified line number.
/// </summary>
void UpdateHighlightingState(int lineNumber);
/// <summary>
/// Notification when the highlighter detects that the highlighting state at the end of a line
/// has changed.
/// This event gets raised for each line as it is processed by the highlighter
/// unless the highlighting state for the line is equal to the old state (when the same line was highlighted previously).
/// </summary>
/// <remarks>
/// For implementers: there is the requirement that, if there was no state changed reported at line X,
/// and there were no document changes between line X and Y (with Y > X), then
/// this event must not be raised for any line between X and Y.
///
/// Equal input state + unchanged line = Equal output state.
///
/// See the comment in the HighlightingColorizer.OnHighlightStateChanged implementation
/// for details about the requirements for a correct custom IHighlighter.
/// </remarks>
event HighlightingStateChangedEventHandler HighlightingStateChanged;
}
/// <summary>
/// Event handler for <see cref="IHighlighter.HighlightingStateChanged"/>
/// </summary>
public delegate void HighlightingStateChangedEventHandler(IHighlighter sender, int lineNumber);
}

Loading…
Cancel
Save