diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
index 0aeaa92216..bd217f36f1 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
@@ -53,10 +53,11 @@ namespace CSharpBinding.Completion
var stringBuilder = new StringBuilder();
var formatter = new ParameterHighlightingOutputFormatter(stringBuilder, highlightedParameterIndex);
ambience.ConvertEntity(Method, formatter, FormattingOptionsFactory.CreateSharpDevelop());
- var inlineBuilder = new HighlightedInlineBuilder(stringBuilder.ToString());
+ string code = stringBuilder.ToString();
+ var inlineBuilder = new RichTextModel();
inlineBuilder.SetFontWeight(formatter.parameterStartOffset, formatter.parameterLength, FontWeights.Bold);
header.Inlines.Clear();
- header.Inlines.AddRange(inlineBuilder.CreateRuns());
+ header.Inlines.AddRange(new RichText(code, inlineBuilder).CreateRuns());
}
public object Content {
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
index 8ae7994ec9..3e95a21c96 100755
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
@@ -50,16 +50,14 @@ namespace ICSharpCode.AvalonEdit.AddIn
// TableRow row = new TableRow();
// trg.Rows.Add(row);
// row.Cells.Add(new TableCell(new Paragraph(new Run(lineNumber.ToString()))) { TextAlignment = TextAlignment.Right });
- HighlightedInlineBuilder inlineBuilder = new HighlightedInlineBuilder(document.GetText(line));
+// Paragraph p = new Paragraph();
+// row.Cells.Add(new TableCell(p));
if (highlighter != null) {
HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber);
- int lineStartOffset = line.Offset;
- foreach (HighlightedSection section in highlightedLine.Sections)
- inlineBuilder.SetHighlighting(section.Offset - lineStartOffset, section.Length, section.Color);
+ p.Inlines.AddRange(highlightedLine.ToRichText().CreateRuns());
+ } else {
+ p.Inlines.Add(document.GetText(line));
}
-// Paragraph p = new Paragraph();
-// row.Cells.Add(new TableCell(p));
- p.Inlines.AddRange(inlineBuilder.CreateRuns());
}
return p;
}
diff --git a/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs b/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
index 17d0ebe002..60315ee17e 100644
--- a/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
+++ b/src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
@@ -68,13 +68,13 @@ namespace SearchAndReplace
RichText displayText = result.DisplayText;
if (displayText != null) {
- HighlightedInlineBuilder builder = new HighlightedInlineBuilder(displayText);
if (IsSelected) {
- builder = builder.Clone();
- builder.SetForeground(0, builder.Text.Length, null);
- builder.SetBackground(0, builder.Text.Length, null);
+ RichTextModel model = displayText.ToRichTextModel();
+ model.SetForeground(0, displayText.Length, null);
+ model.SetBackground(0, displayText.Length, null);
+ displayText = new RichText(displayText.Text, model);
}
- textBlock.Inlines.AddRange(builder.CreateRuns());
+ textBlock.Inlines.AddRange(displayText.CreateRuns());
}
if (showFileName) {
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs
index 42d1e673eb..692b15e7b4 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs
@@ -255,7 +255,7 @@ namespace ICSharpCode.AvalonEdit.Document
///
/// Gets the new offset where the specified offset moves after this document change.
///
- public int GetNewOffset(int oldOffset, AnchorMovementType movementType)
+ public int GetNewOffset(int oldOffset, AnchorMovementType movementType = AnchorMovementType.Default)
{
int insertionLength = this.InsertionLength;
int removalLength = this.RemovalLength;
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedInlineBuilder.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedInlineBuilder.cs
index 4fee0bf8d3..734c053969 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedInlineBuilder.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedInlineBuilder.cs
@@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// into a TextBlock.
/// In SharpDevelop, we use it to provide syntax highlighting inside the search results pad.
///
+ [Obsolete("Use RichText / RichTextModel instead")]
public sealed class HighlightedInlineBuilder
{
static HighlightingBrush MakeBrush(Brush b)
@@ -160,19 +161,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting
}
}
- ///
- /// Sets the font family on the specified text segment.
- ///
- public void SetFontFamily(int offset, int length, FontFamily family)
- {
- int startIndex = GetIndexForOffset(offset);
- int endIndex = GetIndexForOffset(offset + length);
- for (int i = startIndex; i < endIndex; i++) {
- throw new NotSupportedException();
- //stateChanges[i].FontFamily = family;
- }
- }
-
///
/// Creates WPF Run instances that can be used for TextBlock.Inlines.
///
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs
index 0893a7ba1b..90ab30f757 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs
@@ -286,23 +286,36 @@ namespace ICSharpCode.AvalonEdit.Highlighting
///
/// Creates a that stores the text and highlighting of this line.
///
+ [Obsolete("Use ToRichText() instead")]
public HighlightedInlineBuilder ToInlineBuilder()
{
HighlightedInlineBuilder builder = new HighlightedInlineBuilder(Document.GetText(DocumentLine));
int startOffset = DocumentLine.Offset;
- // copy only the foreground and background colors
foreach (HighlightedSection section in Sections) {
builder.SetHighlighting(section.Offset - startOffset, section.Length, section.Color);
}
return builder;
}
+ ///
+ /// Creates a that stores the highlighting of this line.
+ ///
+ public RichTextModel ToRichTextModel()
+ {
+ var builder = new RichTextModel();
+ int startOffset = DocumentLine.Offset;
+ foreach (HighlightedSection section in Sections) {
+ builder.ApplyHighlighting(section.Offset - startOffset, section.Length, section.Color);
+ }
+ return builder;
+ }
+
///
/// Creates a that stores the text and highlighting of this line.
///
public RichText ToRichText()
{
- return ToInlineBuilder().ToRichText();
+ return new RichText(Document.GetText(DocumentLine), ToRichTextModel());
}
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs
index 6632b93603..d518b962b9 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs
@@ -254,5 +254,11 @@ namespace ICSharpCode.AvalonEdit.Highlighting
if (color.background != null)
this.background = color.background;
}
+
+ internal bool IsEmptyForMerge {
+ get {
+ return fontWeight == null && fontStyle == null && foreground == null && background == null;
+ }
+ }
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs
index 104b8505da..46fd4c9902 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs
@@ -127,7 +127,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
///
public RichTextModel ToRichTextModel()
{
- return new RichTextModel(GetHighlightedSections(0, this.Length));
+ return new RichTextModel(stateChangeOffsets, stateChanges);
}
///
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs
index 6517875eb5..ca1f588612 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs
@@ -4,6 +4,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Media;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.AvalonEdit.Document;
@@ -14,52 +16,77 @@ namespace ICSharpCode.AvalonEdit.Highlighting
///
/// Stores rich-text formatting.
///
- public sealed class RichTextModel // TODO: maybe rename to HighlightingModel?
+ public sealed class RichTextModel : AbstractFreezable
{
- CompressingTreeList list = new CompressingTreeList(object.Equals);
+ List stateChangeOffsets = new List();
+ List stateChanges = new List();
- ///
- /// Gets the length of the document.
- /// This has an effect on which coordinates are valid for this RichTextModel.
- ///
- public int DocumentLength {
- get { return list.Count; }
+ int GetIndexForOffset(int offset)
+ {
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException("offset");
+ int index = stateChangeOffsets.BinarySearch(offset);
+ if (index < 0) {
+ // If no color change exists directly at offset,
+ // create a new one.
+ index = ~index;
+ stateChanges.Insert(index, stateChanges[index - 1].Clone());
+ stateChangeOffsets.Insert(index, offset);
+ }
+ return index;
+ }
+
+ int GetIndexForOffsetUseExistingSegment(int offset)
+ {
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException("offset");
+ int index = stateChangeOffsets.BinarySearch(offset);
+ if (index < 0) {
+ // If no color change exists directly at offset,
+ // return the index of the color segment that contains offset.
+ index = ~index - 1;
+ }
+ return index;
+ }
+
+ int GetEnd(int index)
+ {
+ // Gets the end of the color segment no. index.
+ if (index + 1 < stateChangeOffsets.Count)
+ return stateChangeOffsets[index + 1];
+ else
+ return int.MaxValue;
}
///
- /// Creates a new RichTextModel that needs manual calls to .
+ /// Creates a new RichTextModel.
///
- public RichTextModel(int documentLength)
+ public RichTextModel()
{
- list.InsertRange(0, documentLength, HighlightingColor.Empty);
+ stateChangeOffsets.Add(0);
+ stateChanges.Add(new HighlightingColor());
}
///
/// Creates a RichTextModel from a CONTIGUOUS list of HighlightedSections.
///
- internal RichTextModel(IEnumerable sections)
+ internal RichTextModel(int[] stateChangeOffsets, HighlightingColor[] stateChanges)
{
- foreach (var section in sections) {
- list.InsertRange(section.Offset, section.Length, section.Color);
- }
+ this.stateChangeOffsets.AddRange(stateChangeOffsets);
+ this.stateChanges.AddRange(stateChanges);
}
#region UpdateOffsets
///
/// Updates the start and end offsets of all segments stored in this collection.
///
- /// DocumentChangeEventArgs instance describing the change to the document.
- public void UpdateOffsets(DocumentChangeEventArgs e)
+ /// TextChangeEventArgs instance describing the change to the document.
+ public void UpdateOffsets(TextChangeEventArgs e)
{
if (e == null)
throw new ArgumentNullException("e");
- OffsetChangeMap map = e.OffsetChangeMapOrNull;
- if (map != null) {
- foreach (OffsetChangeMapEntry entry in map) {
- UpdateOffsetsInternal(entry);
- }
- } else {
- UpdateOffsetsInternal(e.CreateSingleChangeMapEntry());
+ for (int i = 0; i < stateChangeOffsets.Count; i++) {
+ stateChangeOffsets[i] = e.GetNewOffset(stateChangeOffsets[i]);
}
}
@@ -69,30 +96,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// OffsetChangeMapEntry instance describing the change to the document.
public void UpdateOffsets(OffsetChangeMapEntry change)
{
- UpdateOffsetsInternal(change);
- }
-
- void UpdateOffsetsInternal(OffsetChangeMapEntry entry)
- {
- HighlightingColor color;
- if (entry.RemovalLength > 0) {
- color = list[entry.Offset];
- list.RemoveRange(entry.Offset, entry.RemovalLength);
- } else if (list.Count > 0) {
- color = list[Math.Max(0, entry.Offset - 1)];
- } else {
- color = HighlightingColor.Empty;
+ for (int i = 0; i < stateChangeOffsets.Count; i++) {
+ stateChangeOffsets[i] = change.GetNewOffset(stateChangeOffsets[i]);
}
- list.InsertRange(entry.Offset, entry.InsertionLength, color);
}
#endregion
///
- /// Gets the HighlightingColor for the specified offset.
+ /// Gets a copy of the HighlightingColor for the specified offset.
///
public HighlightingColor GetHighlightingAt(int offset)
{
- return list[offset];
+ return stateChanges[GetIndexForOffsetUseExistingSegment(offset)].Clone();
}
///
@@ -101,12 +116,17 @@ namespace ICSharpCode.AvalonEdit.Highlighting
///
public void ApplyHighlighting(int offset, int length, HighlightingColor color)
{
- list.TransformRange(offset, length, c => {
- var newColor = c.Clone();
- newColor.MergeWith(color);
- newColor.Freeze();
- return newColor;
- });
+ if (color == null || color.IsEmptyForMerge) {
+ // Optimization: don't split the HighlightingState when we're not changing
+ // any property. For example, the "Punctuation" color in C# is
+ // empty by default.
+ return;
+ }
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].MergeWith(color);
+ }
}
///
@@ -115,61 +135,82 @@ namespace ICSharpCode.AvalonEdit.Highlighting
///
public void SetHighlighting(int offset, int length, HighlightingColor color)
{
- list.SetRange(offset, length, FreezableHelper.GetFrozenClone(color));
+ if (length <= 0)
+ return;
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ stateChanges[startIndex] = color != null ? color.Clone() : new HighlightingColor();
+ stateChanges.RemoveRange(startIndex + 1, endIndex - (startIndex + 1));
+ stateChangeOffsets.RemoveRange(startIndex + 1, endIndex - (startIndex + 1));
}
///
- /// Retrieves the highlighted sections in the specified range.
- /// The highlighted sections will be sorted by offset, and there will not be any nested or overlapping sections.
+ /// Sets the foreground brush on the specified text segment.
///
- public IEnumerable GetHighlightedSections(int offset, int length)
+ public void SetForeground(int offset, int length, HighlightingBrush brush)
{
- int pos = offset;
- int endOffset = offset + length;
- while (pos < endOffset) {
- int endPos = Math.Min(endOffset, list.GetEndOfRun(pos));
- yield return new HighlightedSection {
- Offset = pos,
- Length = endPos - pos,
- Color = list[pos]
- };
- pos = endPos;
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Foreground = brush;
}
}
- #region WriteDocumentTo
///
- /// Writes the specified document, with the formatting from this rich text model applied,
- /// to the RichTextWriter.
+ /// Sets the background brush on the specified text segment.
///
- public void WriteDocumentTo(ITextSource document, RichTextWriter writer)
+ public void SetBackground(int offset, int length, HighlightingBrush brush)
{
- WriteDocumentTo(document, new SimpleSegment(0, DocumentLength), writer);
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].Background = brush;
+ }
}
///
- /// Writes a segment of the specified document, with the formatting from this rich text model applied,
- /// to the RichTextWriter.
+ /// Sets the font weight on the specified text segment.
///
- public void WriteDocumentTo(ITextSource document, ISegment segment, RichTextWriter writer)
+ public void SetFontWeight(int offset, int length, FontWeight weight)
{
- if (document == null)
- throw new ArgumentNullException("document");
- if (segment == null)
- throw new ArgumentNullException("segment");
- if (writer == null)
- throw new ArgumentNullException("writer");
-
- int pos = segment.Offset;
- int endOffset = segment.EndOffset;
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].FontWeight = weight;
+ }
+ }
+
+ ///
+ /// Sets the font style on the specified text segment.
+ ///
+ public void SetFontStyle(int offset, int length, FontStyle style)
+ {
+ int startIndex = GetIndexForOffset(offset);
+ int endIndex = GetIndexForOffset(offset + length);
+ for (int i = startIndex; i < endIndex; i++) {
+ stateChanges[i].FontStyle = style;
+ }
+ }
+
+ ///
+ /// Retrieves the highlighted sections in the specified range.
+ /// The highlighted sections will be sorted by offset, and there will not be any nested or overlapping sections.
+ ///
+ public IEnumerable GetHighlightedSections(int offset, int length)
+ {
+ int index = GetIndexForOffsetUseExistingSegment(offset);
+ int pos = offset;
+ int endOffset = offset + length;
while (pos < endOffset) {
- int endPos = Math.Min(endOffset, list.GetEndOfRun(pos));
- writer.BeginSpan(list[pos]);
- document.WriteTextTo(writer, pos, endPos - pos);
- writer.EndSpan();
+ int endPos = Math.Min(endOffset, GetEnd(index));
+ yield return new HighlightedSection {
+ Offset = pos,
+ Length = endPos - pos,
+ Color = stateChanges[index].Clone()
+ };
pos = endPos;
+ index++;
}
}
- #endregion
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModelWriter.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModelWriter.cs
index a1abc2aa97..777fea51e1 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModelWriter.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModelWriter.cs
@@ -34,10 +34,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
throw new ArgumentNullException("richTextModel");
this.richTextModel = richTextModel;
this.documentTextWriter = (DocumentTextWriter)base.textWriter;
- if (richTextModel.DocumentLength == 0)
- currentColor = HighlightingColor.Empty;
- else
- currentColor = richTextModel.GetHighlightingAt(Math.Max(0, insertionOffset - 1));
+ currentColor = richTextModel.GetHighlightingAt(Math.Max(0, insertionOffset - 1));
}
///
diff --git a/src/Main/Base/Project/Editor/Search/SearchResultsPad.cs b/src/Main/Base/Project/Editor/Search/SearchResultsPad.cs
index ee52b1a165..59c2002fee 100644
--- a/src/Main/Base/Project/Editor/Search/SearchResultsPad.cs
+++ b/src/Main/Base/Project/Editor/Search/SearchResultsPad.cs
@@ -152,21 +152,23 @@ namespace ICSharpCode.SharpDevelop.Editor.Search
public static RichText CreateInlineBuilder(TextLocation startPosition, TextLocation endPosition, IDocument document, IHighlighter highlighter)
{
if (startPosition.Line >= 1 && startPosition.Line <= document.LineCount) {
- var inlineBuilder = highlighter.HighlightLine(startPosition.Line).ToInlineBuilder();
+ var highlightedLine = highlighter.HighlightLine(startPosition.Line);
+ var documentLine = highlightedLine.DocumentLine;
+ var inlineBuilder = highlightedLine.ToRichTextModel();
// reset bold/italics
- inlineBuilder.SetFontWeight(0, inlineBuilder.Text.Length, FontWeights.Normal);
- inlineBuilder.SetFontStyle(0, inlineBuilder.Text.Length, FontStyles.Normal);
+ inlineBuilder.SetFontWeight(0, documentLine.Length, FontWeights.Normal);
+ inlineBuilder.SetFontStyle(0, documentLine.Length, FontStyles.Normal);
// now highlight the match in bold
if (startPosition.Column >= 1) {
if (endPosition.Line == startPosition.Line && endPosition.Column > startPosition.Column) {
// subtract one from the column to get the offset inside the line's text
int startOffset = startPosition.Column - 1;
- int endOffset = Math.Min(inlineBuilder.Text.Length, endPosition.Column - 1);
+ int endOffset = Math.Min(documentLine.Length, endPosition.Column - 1);
inlineBuilder.SetFontWeight(startOffset, endOffset - startOffset, FontWeights.Bold);
}
}
- return inlineBuilder.ToRichText();
+ return new RichText(document.GetText(documentLine), inlineBuilder);
}
return null;
}