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 @@ -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 {

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

@ -50,16 +50,14 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -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;
}

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

@ -68,13 +68,13 @@ namespace SearchAndReplace @@ -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) {

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

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

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

@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -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.
/// </remarks>
[Obsolete("Use RichText / RichTextModel instead")]
public sealed class HighlightedInlineBuilder
{
static HighlightingBrush MakeBrush(Brush b)
@ -160,19 +161,6 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -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>
/// Creates WPF Run instances that can be used for TextBlock.Inlines.
/// </summary>

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

@ -286,23 +286,36 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -286,23 +286,36 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <summary>
/// Creates a <see cref="HighlightedInlineBuilder"/> that stores the text and highlighting of this line.
/// </summary>
[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;
}
/// <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>
/// Creates a <see cref="RichText"/> that stores the text and highlighting of this line.
/// </summary>
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 @@ -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;
}
}
}
}

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

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

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

@ -4,6 +4,8 @@ @@ -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 @@ -14,52 +16,77 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <summary>
/// Stores rich-text formatting.
/// </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>
/// Gets the length of the document.
/// This has an effect on which coordinates are valid for this RichTextModel.
/// </summary>
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;
}
/// <summary>
/// Creates a new RichTextModel that needs manual calls to <see cref="UpdateOffsets(DocumentChangeEventArgs)"/>.
/// Creates a new RichTextModel.
/// </summary>
public RichTextModel(int documentLength)
public RichTextModel()
{
list.InsertRange(0, documentLength, HighlightingColor.Empty);
stateChangeOffsets.Add(0);
stateChanges.Add(new HighlightingColor());
}
/// <summary>
/// Creates a RichTextModel from a CONTIGUOUS list of HighlightedSections.
/// </summary>
internal RichTextModel(IEnumerable<HighlightedSection> 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
/// <summary>
/// Updates the start and end offsets of all segments stored in this collection.
/// </summary>
/// <param name="e">DocumentChangeEventArgs instance describing the change to the document.</param>
public void UpdateOffsets(DocumentChangeEventArgs e)
/// <param name="e">TextChangeEventArgs instance describing the change to the document.</param>
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 @@ -69,30 +96,18 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// <param name="change">OffsetChangeMapEntry instance describing the change to the document.</param>
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
/// <summary>
/// Gets the HighlightingColor for the specified offset.
/// Gets a copy of the HighlightingColor for the specified offset.
/// </summary>
public HighlightingColor GetHighlightingAt(int offset)
{
return list[offset];
return stateChanges[GetIndexForOffsetUseExistingSegment(offset)].Clone();
}
/// <summary>
@ -101,12 +116,17 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -101,12 +116,17 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary>
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);
}
}
/// <summary>
@ -115,61 +135,82 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -115,61 +135,82 @@ namespace ICSharpCode.AvalonEdit.Highlighting
/// </summary>
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>
/// 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.
/// </summary>
public IEnumerable<HighlightedSection> 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
/// <summary>
/// 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.
/// </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>
/// 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.
/// </summary>
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;
}
}
/// <summary>
/// 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) {
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
}
}

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

@ -34,10 +34,7 @@ namespace ICSharpCode.AvalonEdit.Highlighting @@ -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));
}
/// <summary>

12
src/Main/Base/Project/Editor/Search/SearchResultsPad.cs

@ -152,21 +152,23 @@ namespace ICSharpCode.SharpDevelop.Editor.Search @@ -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;
}

Loading…
Cancel
Save