diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
index d572f6c8b0..ac84b63f8b 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
@@ -342,6 +342,7 @@
+
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs
index 83a2184540..f0fd8d1be0 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/ITextRunConstructionContext.cs
@@ -4,6 +4,7 @@
using System;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Rendering
{
@@ -31,5 +32,16 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// Gets the global text run properties.
///
TextRunProperties GlobalTextRunProperties { get; }
+
+ ///
+ /// Gets a piece of text from the document.
+ ///
+ ///
+ /// This method is allowed to return a larger string than requested.
+ /// It does this by returning a 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.
+ ///
+ StringSegment GetText(int offset, int length);
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs
index 0040decd12..70d59358ae 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/LinkElementGenerator.cs
+++ b/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)
using System;
-using System.Diagnostics;
using System.Text.RegularExpressions;
-using System.Windows;
-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;
+using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Rendering
{
@@ -68,8 +60,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
Match GetMatch(int startOffset)
{
int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
- string relevantText = CurrentContext.Document.GetText(startOffset, endOffset - startOffset);
- return linkRegex.Match(relevantText);
+ StringSegment relevantText = CurrentContext.GetText(startOffset, endOffset - startOffset);
+ return linkRegex.Match(relevantText.Text, relevantText.Offset, relevantText.Count);
}
///
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs
index 3d26e7c9b9..4ff5def317 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/SingleCharacterElementGenerator.cs
@@ -60,10 +60,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
public override int GetFirstInterestedOffset(int startOffset)
{
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++) {
- char c = relevantText[i];
+ int endPos = relevantText.Offset + relevantText.Count;
+ for (int i = relevantText.Offset; i < endPos; i++) {
+ char c = relevantText.Text[i];
switch (c) {
case ' ':
if (ShowSpaces)
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs
index 3cefe1e264..f5d7ae19ed 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineText.cs
@@ -53,8 +53,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
throw new ArgumentNullException("context");
int relativeOffset = startVisualColumn - VisualColumn;
- string text = context.Document.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset);
- return new TextCharacters(text, 0, text.Length, this.TextRunProperties);
+ StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset + relativeOffset, DocumentLength - relativeOffset);
+ return new TextCharacters(text.Text, text.Offset, text.Count, this.TextRunProperties);
}
///
@@ -71,8 +71,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
throw new ArgumentNullException("context");
int relativeOffset = visualColumnLimit - VisualColumn;
- string text = context.Document.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset);
- CharacterBufferRange range = new CharacterBufferRange(text, 0, text.Length);
+ StringSegment text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset);
+ CharacterBufferRange range = new CharacterBufferRange(text.Text, text.Offset, text.Count);
return new TextSpan(range.Length, new CultureSpecificCharacterBufferRange(this.TextRunProperties.CultureInfo, range));
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs
index eea016446f..375a09bf9e 100644
--- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs
+++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs
@@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Windows.Media.TextFormatting;
using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Utils;
namespace ICSharpCode.AvalonEdit.Rendering
{
@@ -83,5 +84,19 @@ namespace ICSharpCode.AvalonEdit.Rendering
{
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));
+ }
}
}
diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/StringSegment.cs
new file mode 100644
index 0000000000..8a39ea874f
--- /dev/null
+++ b/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
+{
+ ///
+ /// Represents a string with a segment.
+ /// Similar to System.ArraySegment<T>, but for strings instead of arrays.
+ ///
+ public struct StringSegment : IEquatable
+ {
+ readonly string text;
+ readonly int offset;
+ readonly int count;
+
+ ///
+ /// Creates a new StringSegment.
+ ///
+ 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;
+ }
+
+ ///
+ /// Creates a new StringSegment.
+ ///
+ public StringSegment(string text)
+ {
+ if (text == null)
+ throw new ArgumentNullException("text");
+ this.text = text;
+ this.offset = 0;
+ this.count = text.Length;
+ }
+
+ ///
+ /// Gets the string used for this segment.
+ ///
+ public string Text {
+ get { return text; }
+ }
+
+ ///
+ /// Gets the start offset of the segment with the text.
+ ///
+ public int Offset {
+ get { return offset; }
+ }
+
+ ///
+ /// Gets the length of the segment.
+ ///
+ public int Count {
+ get { return count; }
+ }
+
+ #region Equals and GetHashCode implementation
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj is StringSegment)
+ return Equals((StringSegment)obj); // use Equals method below
+ else
+ return false;
+ }
+
+ ///
+ public bool Equals(StringSegment other)
+ {
+ // add comparisions for all members here
+ return object.ReferenceEquals(this.text, other.text) && offset == other.offset && count == other.count;
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return text.GetHashCode() ^ offset ^ count;
+ }
+
+ ///
+ /// Equality operator.
+ ///
+ public static bool operator ==(StringSegment left, StringSegment right)
+ {
+ return left.Equals(right);
+ }
+
+ ///
+ /// Inequality operator.
+ ///
+ public static bool operator !=(StringSegment left, StringSegment right)
+ {
+ return !left.Equals(right);
+ }
+ #endregion
+ }
+}