Browse Source

Replace HighlightedInlineBuilder with RichTextModel.

pull/59/merge
Daniel Grunwald 12 years ago
parent
commit
0aee08dc57
  1. 5
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
  2. 12
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs
  3. 10
      src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs
  4. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs
  5. 14
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedInlineBuilder.cs
  6. 17
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedLine.cs
  7. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs
  8. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs
  9. 205
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs
  10. 5
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModelWriter.cs
  11. 12
      src/Main/Base/Project/Editor/Search/SearchResultsPad.cs

5
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs

@ -53,10 +53,11 @@ namespace CSharpBinding.Completion
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
var formatter = new ParameterHighlightingOutputFormatter(stringBuilder, highlightedParameterIndex); var formatter = new ParameterHighlightingOutputFormatter(stringBuilder, highlightedParameterIndex);
ambience.ConvertEntity(Method, formatter, FormattingOptionsFactory.CreateSharpDevelop()); 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); inlineBuilder.SetFontWeight(formatter.parameterStartOffset, formatter.parameterLength, FontWeights.Bold);
header.Inlines.Clear(); header.Inlines.Clear();
header.Inlines.AddRange(inlineBuilder.CreateRuns()); header.Inlines.AddRange(new RichText(code, inlineBuilder).CreateRuns());
} }
public object Content { public object Content {

12
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/DocumentPrinter.cs

@ -50,16 +50,14 @@ namespace ICSharpCode.AvalonEdit.AddIn
// TableRow row = new TableRow(); // TableRow row = new TableRow();
// trg.Rows.Add(row); // trg.Rows.Add(row);
// row.Cells.Add(new TableCell(new Paragraph(new Run(lineNumber.ToString()))) { TextAlignment = TextAlignment.Right }); // 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) { if (highlighter != null) {
HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber); HighlightedLine highlightedLine = highlighter.HighlightLine(lineNumber);
int lineStartOffset = line.Offset; p.Inlines.AddRange(highlightedLine.ToRichText().CreateRuns());
foreach (HighlightedSection section in highlightedLine.Sections) } else {
inlineBuilder.SetHighlighting(section.Offset - lineStartOffset, section.Length, section.Color); p.Inlines.Add(document.GetText(line));
} }
// Paragraph p = new Paragraph();
// row.Cells.Add(new TableCell(p));
p.Inlines.AddRange(inlineBuilder.CreateRuns());
} }
return p; return p;
} }

10
src/AddIns/Misc/SearchAndReplace/Project/Gui/SearchResultNode.cs

@ -68,13 +68,13 @@ namespace SearchAndReplace
RichText displayText = result.DisplayText; RichText displayText = result.DisplayText;
if (displayText != null) { if (displayText != null) {
HighlightedInlineBuilder builder = new HighlightedInlineBuilder(displayText);
if (IsSelected) { if (IsSelected) {
builder = builder.Clone(); RichTextModel model = displayText.ToRichTextModel();
builder.SetForeground(0, builder.Text.Length, null); model.SetForeground(0, displayText.Length, null);
builder.SetBackground(0, builder.Text.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) { if (showFileName) {

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/OffsetChangeMap.cs

@ -255,7 +255,7 @@ namespace ICSharpCode.AvalonEdit.Document
/// <summary> /// <summary>
/// Gets the new offset where the specified offset moves after this document change. /// Gets the new offset where the specified offset moves after this document change.
/// </summary> /// </summary>
public int GetNewOffset(int oldOffset, AnchorMovementType movementType) public int GetNewOffset(int oldOffset, AnchorMovementType movementType = AnchorMovementType.Default)
{ {
int insertionLength = this.InsertionLength; int insertionLength = this.InsertionLength;
int removalLength = this.RemovalLength; int removalLength = this.RemovalLength;

14
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightedInlineBuilder.cs

@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// into a TextBlock. /// into a TextBlock.
/// In SharpDevelop, we use it to provide syntax highlighting inside the search results pad. /// In SharpDevelop, we use it to provide syntax highlighting inside the search results pad.
/// </remarks> /// </remarks>
[Obsolete("Use RichText / RichTextModel instead")]
public sealed class HighlightedInlineBuilder public sealed class HighlightedInlineBuilder
{ {
static HighlightingBrush MakeBrush(Brush b) static HighlightingBrush MakeBrush(Brush b)
@ -160,19 +161,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting
} }
} }
/// <summary>
/// Sets the font family on the specified text segment.
/// </summary>
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;
}
}
/// <summary> /// <summary>
/// Creates WPF Run instances that can be used for TextBlock.Inlines. /// Creates WPF Run instances that can be used for TextBlock.Inlines.
/// </summary> /// </summary>

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

@ -286,23 +286,36 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <summary> /// <summary>
/// Creates a <see cref="HighlightedInlineBuilder"/> that stores the text and highlighting of this line. /// Creates a <see cref="HighlightedInlineBuilder"/> that stores the text and highlighting of this line.
/// </summary> /// </summary>
[Obsolete("Use ToRichText() instead")]
public HighlightedInlineBuilder ToInlineBuilder() public HighlightedInlineBuilder ToInlineBuilder()
{ {
HighlightedInlineBuilder builder = new HighlightedInlineBuilder(Document.GetText(DocumentLine)); HighlightedInlineBuilder builder = new HighlightedInlineBuilder(Document.GetText(DocumentLine));
int startOffset = DocumentLine.Offset; int startOffset = DocumentLine.Offset;
// copy only the foreground and background colors
foreach (HighlightedSection section in Sections) { foreach (HighlightedSection section in Sections) {
builder.SetHighlighting(section.Offset - startOffset, section.Length, section.Color); builder.SetHighlighting(section.Offset - startOffset, section.Length, section.Color);
} }
return builder; return builder;
} }
/// <summary>
/// Creates a <see cref="RichTextModel"/> that stores the highlighting of this line.
/// </summary>
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;
}
/// <summary> /// <summary>
/// Creates a <see cref="RichText"/> that stores the text and highlighting of this line. /// Creates a <see cref="RichText"/> that stores the text and highlighting of this line.
/// </summary> /// </summary>
public RichText ToRichText() public RichText ToRichText()
{ {
return ToInlineBuilder().ToRichText(); return new RichText(Document.GetText(DocumentLine), ToRichTextModel());
} }
} }
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/HighlightingColor.cs

@ -254,5 +254,11 @@ namespace ICSharpCode.AvalonEdit.Highlighting
if (color.background != null) if (color.background != null)
this.background = color.background; this.background = color.background;
} }
internal bool IsEmptyForMerge {
get {
return fontWeight == null && fontStyle == null && foreground == null && background == null;
}
}
} }
} }

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichText.cs

@ -127,7 +127,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary> /// </summary>
public RichTextModel ToRichTextModel() public RichTextModel ToRichTextModel()
{ {
return new RichTextModel(GetHighlightedSections(0, this.Length)); return new RichTextModel(stateChangeOffsets, stateChanges);
} }
/// <summary> /// <summary>

205
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModel.cs

@ -4,6 +4,8 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
@ -14,52 +16,77 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <summary> /// <summary>
/// Stores rich-text formatting. /// Stores rich-text formatting.
/// </summary> /// </summary>
public sealed class RichTextModel // TODO: maybe rename to HighlightingModel? public sealed class RichTextModel : AbstractFreezable
{ {
CompressingTreeList<HighlightingColor> list = new CompressingTreeList<HighlightingColor>(object.Equals); List<int> stateChangeOffsets = new List<int>();
List<HighlightingColor> stateChanges = new List<HighlightingColor>();
/// <summary> int GetIndexForOffset(int offset)
/// Gets the length of the document. {
/// This has an effect on which coordinates are valid for this RichTextModel. if (offset < 0)
/// </summary> throw new ArgumentOutOfRangeException("offset");
public int DocumentLength { int index = stateChangeOffsets.BinarySearch(offset);
get { return list.Count; } 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;
} }
/// <summary> /// <summary>
/// Creates a new RichTextModel that needs manual calls to <see cref="UpdateOffsets(DocumentChangeEventArgs)"/>. /// Creates a new RichTextModel.
/// </summary> /// </summary>
public RichTextModel(int documentLength) public RichTextModel()
{ {
list.InsertRange(0, documentLength, HighlightingColor.Empty); stateChangeOffsets.Add(0);
stateChanges.Add(new HighlightingColor());
} }
/// <summary> /// <summary>
/// Creates a RichTextModel from a CONTIGUOUS list of HighlightedSections. /// Creates a RichTextModel from a CONTIGUOUS list of HighlightedSections.
/// </summary> /// </summary>
internal RichTextModel(IEnumerable<HighlightedSection> sections) internal RichTextModel(int[] stateChangeOffsets, HighlightingColor[] stateChanges)
{ {
foreach (var section in sections) { this.stateChangeOffsets.AddRange(stateChangeOffsets);
list.InsertRange(section.Offset, section.Length, section.Color); this.stateChanges.AddRange(stateChanges);
}
} }
#region UpdateOffsets #region UpdateOffsets
/// <summary> /// <summary>
/// Updates the start and end offsets of all segments stored in this collection. /// Updates the start and end offsets of all segments stored in this collection.
/// </summary> /// </summary>
/// <param name="e">DocumentChangeEventArgs instance describing the change to the document.</param> /// <param name="e">TextChangeEventArgs instance describing the change to the document.</param>
public void UpdateOffsets(DocumentChangeEventArgs e) public void UpdateOffsets(TextChangeEventArgs e)
{ {
if (e == null) if (e == null)
throw new ArgumentNullException("e"); throw new ArgumentNullException("e");
OffsetChangeMap map = e.OffsetChangeMapOrNull; for (int i = 0; i < stateChangeOffsets.Count; i++) {
if (map != null) { stateChangeOffsets[i] = e.GetNewOffset(stateChangeOffsets[i]);
foreach (OffsetChangeMapEntry entry in map) {
UpdateOffsetsInternal(entry);
}
} else {
UpdateOffsetsInternal(e.CreateSingleChangeMapEntry());
} }
} }
@ -69,30 +96,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <param name="change">OffsetChangeMapEntry instance describing the change to the document.</param> /// <param name="change">OffsetChangeMapEntry instance describing the change to the document.</param>
public void UpdateOffsets(OffsetChangeMapEntry change) public void UpdateOffsets(OffsetChangeMapEntry change)
{ {
UpdateOffsetsInternal(change); for (int i = 0; i < stateChangeOffsets.Count; i++) {
} stateChangeOffsets[i] = change.GetNewOffset(stateChangeOffsets[i]);
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;
} }
list.InsertRange(entry.Offset, entry.InsertionLength, color);
} }
#endregion #endregion
/// <summary> /// <summary>
/// Gets the HighlightingColor for the specified offset. /// Gets a copy of the HighlightingColor for the specified offset.
/// </summary> /// </summary>
public HighlightingColor GetHighlightingAt(int offset) public HighlightingColor GetHighlightingAt(int offset)
{ {
return list[offset]; return stateChanges[GetIndexForOffsetUseExistingSegment(offset)].Clone();
} }
/// <summary> /// <summary>
@ -101,12 +116,17 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary> /// </summary>
public void ApplyHighlighting(int offset, int length, HighlightingColor color) public void ApplyHighlighting(int offset, int length, HighlightingColor color)
{ {
list.TransformRange(offset, length, c => { if (color == null || color.IsEmptyForMerge) {
var newColor = c.Clone(); // Optimization: don't split the HighlightingState when we're not changing
newColor.MergeWith(color); // any property. For example, the "Punctuation" color in C# is
newColor.Freeze(); // empty by default.
return newColor; return;
}); }
int startIndex = GetIndexForOffset(offset);
int endIndex = GetIndexForOffset(offset + length);
for (int i = startIndex; i < endIndex; i++) {
stateChanges[i].MergeWith(color);
}
} }
/// <summary> /// <summary>
@ -115,61 +135,82 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary> /// </summary>
public void SetHighlighting(int offset, int length, HighlightingColor color) 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));
} }
/// <summary> /// <summary>
/// Retrieves the highlighted sections in the specified range. /// Sets the foreground brush on the specified text segment.
/// The highlighted sections will be sorted by offset, and there will not be any nested or overlapping sections.
/// </summary> /// </summary>
public IEnumerable<HighlightedSection> GetHighlightedSections(int offset, int length) public void SetForeground(int offset, int length, HighlightingBrush brush)
{ {
int pos = offset; int startIndex = GetIndexForOffset(offset);
int endOffset = offset + length; int endIndex = GetIndexForOffset(offset + length);
while (pos < endOffset) { for (int i = startIndex; i < endIndex; i++) {
int endPos = Math.Min(endOffset, list.GetEndOfRun(pos)); stateChanges[i].Foreground = brush;
yield return new HighlightedSection {
Offset = pos,
Length = endPos - pos,
Color = list[pos]
};
pos = endPos;
} }
} }
#region WriteDocumentTo
/// <summary> /// <summary>
/// Writes the specified document, with the formatting from this rich text model applied, /// Sets the background brush on the specified text segment.
/// to the RichTextWriter.
/// </summary> /// </summary>
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;
}
} }
/// <summary> /// <summary>
/// Writes a segment of the specified document, with the formatting from this rich text model applied, /// Sets the font weight on the specified text segment.
/// to the RichTextWriter.
/// </summary> /// </summary>
public void WriteDocumentTo(ITextSource document, ISegment segment, RichTextWriter writer) public void SetFontWeight(int offset, int length, FontWeight weight)
{ {
if (document == null) int startIndex = GetIndexForOffset(offset);
throw new ArgumentNullException("document"); int endIndex = GetIndexForOffset(offset + length);
if (segment == null) for (int i = startIndex; i < endIndex; i++) {
throw new ArgumentNullException("segment"); stateChanges[i].FontWeight = weight;
if (writer == null) }
throw new ArgumentNullException("writer"); }
int pos = segment.Offset; /// <summary>
int endOffset = segment.EndOffset; /// Sets the font style on the specified text segment.
/// </summary>
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;
}
}
/// <summary>
/// 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.
/// </summary>
public IEnumerable<HighlightedSection> GetHighlightedSections(int offset, int length)
{
int index = GetIndexForOffsetUseExistingSegment(offset);
int pos = offset;
int endOffset = offset + length;
while (pos < endOffset) { while (pos < endOffset) {
int endPos = Math.Min(endOffset, list.GetEndOfRun(pos)); int endPos = Math.Min(endOffset, GetEnd(index));
writer.BeginSpan(list[pos]); yield return new HighlightedSection {
document.WriteTextTo(writer, pos, endPos - pos); Offset = pos,
writer.EndSpan(); Length = endPos - pos,
Color = stateChanges[index].Clone()
};
pos = endPos; pos = endPos;
index++;
} }
} }
#endregion
} }
} }

5
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Highlighting/RichTextModelWriter.cs

@ -34,10 +34,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting
throw new ArgumentNullException("richTextModel"); throw new ArgumentNullException("richTextModel");
this.richTextModel = richTextModel; this.richTextModel = richTextModel;
this.documentTextWriter = (DocumentTextWriter)base.textWriter; this.documentTextWriter = (DocumentTextWriter)base.textWriter;
if (richTextModel.DocumentLength == 0) currentColor = richTextModel.GetHighlightingAt(Math.Max(0, insertionOffset - 1));
currentColor = HighlightingColor.Empty;
else
currentColor = richTextModel.GetHighlightingAt(Math.Max(0, insertionOffset - 1));
} }
/// <summary> /// <summary>

12
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) public static RichText CreateInlineBuilder(TextLocation startPosition, TextLocation endPosition, IDocument document, IHighlighter highlighter)
{ {
if (startPosition.Line >= 1 && startPosition.Line <= document.LineCount) { 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 // reset bold/italics
inlineBuilder.SetFontWeight(0, inlineBuilder.Text.Length, FontWeights.Normal); inlineBuilder.SetFontWeight(0, documentLine.Length, FontWeights.Normal);
inlineBuilder.SetFontStyle(0, inlineBuilder.Text.Length, FontStyles.Normal); inlineBuilder.SetFontStyle(0, documentLine.Length, FontStyles.Normal);
// now highlight the match in bold // now highlight the match in bold
if (startPosition.Column >= 1) { if (startPosition.Column >= 1) {
if (endPosition.Line == startPosition.Line && endPosition.Column > startPosition.Column) { if (endPosition.Line == startPosition.Line && endPosition.Column > startPosition.Column) {
// subtract one from the column to get the offset inside the line's text // subtract one from the column to get the offset inside the line's text
int startOffset = startPosition.Column - 1; 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); inlineBuilder.SetFontWeight(startOffset, endOffset - startOffset, FontWeights.Bold);
} }
} }
return inlineBuilder.ToRichText(); return new RichText(document.GetText(documentLine), inlineBuilder);
} }
return null; return null;
} }

Loading…
Cancel
Save