Browse Source

Reduce memory usage when dealing with long lines and word-wrapping.

4.0
Daniel Grunwald 15 years ago
parent
commit
b7b12d310e
  1. 1
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  2. 12
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs
  3. 14
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs
  4. 7
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs
  5. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs
  6. 15
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs
  7. 107
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs

1
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -342,6 +342,7 @@
<Compile Include="Utils\Rope.cs" /> <Compile Include="Utils\Rope.cs" />
<Compile Include="Utils\RopeNode.cs" /> <Compile Include="Utils\RopeNode.cs" />
<Compile Include="Utils\RopeTextReader.cs" /> <Compile Include="Utils\RopeTextReader.cs" />
<Compile Include="Utils\StringSegment.cs" />
<Compile Include="Utils\TextFormatterFactory.cs" /> <Compile Include="Utils\TextFormatterFactory.cs" />
<Compile Include="Utils\WeakEventManagerBase.cs" /> <Compile Include="Utils\WeakEventManagerBase.cs" />
<Compile Include="Utils\PixelSnapHelpers.cs" /> <Compile Include="Utils\PixelSnapHelpers.cs" />

12
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Rendering namespace ICSharpCode.AvalonEdit.Rendering
{ {
@ -31,5 +32,16 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// Gets the global text run properties. /// Gets the global text run properties.
/// </summary> /// </summary>
TextRunProperties GlobalTextRunProperties { get; } TextRunProperties GlobalTextRunProperties { get; }
/// <summary>
/// Gets a piece of text from the document.
/// </summary>
/// <remarks>
/// This method is allowed to return a larger string than requested.
/// It does this by returning a <see cref="StringSegment"/> that describes the requested segment within the returned string.
/// This method should be the preferred text access method in the text transformation pipeline, as it can avoid repeatedly allocating string instances
/// for text within the same line.
/// </remarks>
StringSegment GetText(int offset, int length);
} }
} }

14
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs

@ -2,16 +2,8 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Diagnostics;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows; using ICSharpCode.AvalonEdit.Utils;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document;
using System.Windows.Navigation;
namespace ICSharpCode.AvalonEdit.Rendering namespace ICSharpCode.AvalonEdit.Rendering
{ {
@ -68,8 +60,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
Match GetMatch(int startOffset) Match GetMatch(int startOffset)
{ {
int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset; int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
string relevantText = CurrentContext.Document.GetText(startOffset, endOffset - startOffset); StringSegment relevantText = CurrentContext.GetText(startOffset, endOffset - startOffset);
return linkRegex.Match(relevantText); return linkRegex.Match(relevantText.Text, relevantText.Offset, relevantText.Count);
} }
/// <inheritdoc/> /// <inheritdoc/>

7
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs

@ -60,10 +60,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
public override int GetFirstInterestedOffset(int startOffset) public override int GetFirstInterestedOffset(int startOffset)
{ {
DocumentLine endLine = CurrentContext.VisualLine.LastDocumentLine; DocumentLine endLine = CurrentContext.VisualLine.LastDocumentLine;
string relevantText = CurrentContext.Document.GetText(startOffset, endLine.EndOffset - startOffset); StringSegment relevantText = CurrentContext.GetText(startOffset, endLine.EndOffset - startOffset);
for (int i = 0; i < relevantText.Length; i++) { int endPos = relevantText.Offset + relevantText.Count;
char c = relevantText[i]; for (int i = relevantText.Offset; i < endPos; i++) {
char c = relevantText.Text[i];
switch (c) { switch (c) {
case ' ': case ' ':
if (ShowSpaces) if (ShowSpaces)

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs

@ -53,8 +53,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
int relativeOffset = startVisualColumn - VisualColumn; int relativeOffset = startVisualColumn - VisualColumn;
string text = context.Document.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset); StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset);
return new TextCharacters(text, 0, text.Length, this.TextRunProperties); return new TextCharacters(text.Text, text.Offset, text.Count, this.TextRunProperties);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -71,8 +71,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
int relativeOffset = visualColumnLimit - VisualColumn; int relativeOffset = visualColumnLimit - VisualColumn;
string text = context.Document.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset); StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset);
CharacterBufferRange range = new CharacterBufferRange(text, 0, text.Length); CharacterBufferRange range = new CharacterBufferRange(text.Text, text.Offset, text.Count);
return new TextSpan<CultureSpecificCharacterBufferRange>(range.Length, new CultureSpecificCharacterBufferRange(this.TextRunProperties.CultureInfo, range)); return new TextSpan<CultureSpecificCharacterBufferRange>(range.Length, new CultureSpecificCharacterBufferRange(this.TextRunProperties.CultureInfo, range));
} }

15
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Windows.Media.TextFormatting; using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Rendering namespace ICSharpCode.AvalonEdit.Rendering
{ {
@ -83,5 +84,19 @@ namespace ICSharpCode.AvalonEdit.Rendering
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
string cachedString;
int cachedStringOffset;
public StringSegment GetText(int offset, int length)
{
if (cachedString != null) {
if (offset >= cachedStringOffset && offset + length <= cachedStringOffset + cachedString.Length) {
return new StringSegment(cachedString, offset - cachedStringOffset, length);
}
}
cachedStringOffset = offset;
return new StringSegment(cachedString = this.Document.GetText(offset, length));
}
} }
} }

107
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs

@ -0,0 +1,107 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.AvalonEdit.Utils
{
/// <summary>
/// Represents a string with a segment.
/// Similar to System.ArraySegment&lt;T&gt;, but for strings instead of arrays.
/// </summary>
public struct StringSegment : IEquatable<StringSegment>
{
readonly string text;
readonly int offset;
readonly int count;
/// <summary>
/// Creates a new StringSegment.
/// </summary>
public StringSegment(string text, int offset, int count)
{
if (text == null)
throw new ArgumentNullException("text");
if (offset < 0 || offset > text.Length)
throw new ArgumentOutOfRangeException("offset");
if (offset + count > text.Length)
throw new ArgumentOutOfRangeException("count");
this.text = text;
this.offset = offset;
this.count = count;
}
/// <summary>
/// Creates a new StringSegment.
/// </summary>
public StringSegment(string text)
{
if (text == null)
throw new ArgumentNullException("text");
this.text = text;
this.offset = 0;
this.count = text.Length;
}
/// <summary>
/// Gets the string used for this segment.
/// </summary>
public string Text {
get { return text; }
}
/// <summary>
/// Gets the start offset of the segment with the text.
/// </summary>
public int Offset {
get { return offset; }
}
/// <summary>
/// Gets the length of the segment.
/// </summary>
public int Count {
get { return count; }
}
#region Equals and GetHashCode implementation
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is StringSegment)
return Equals((StringSegment)obj); // use Equals method below
else
return false;
}
/// <inheritdoc/>
public bool Equals(StringSegment other)
{
// add comparisions for all members here
return object.ReferenceEquals(this.text, other.text) && offset == other.offset && count == other.count;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return text.GetHashCode() ^ offset ^ count;
}
/// <summary>
/// Equality operator.
/// </summary>
public static bool operator ==(StringSegment left, StringSegment right)
{
return left.Equals(right);
}
/// <summary>
/// Inequality operator.
/// </summary>
public static bool operator !=(StringSegment left, StringSegment right)
{
return !left.Equals(right);
}
#endregion
}
}
Loading…
Cancel
Save