@ -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 TextView DocumentHighlighter( 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
* /
}
}
}
}