Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New implementation of HeightTree #371

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Setup()
document.Text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10";
heightTree = new HeightTree(document, 10);
foreach (DocumentLine line in document.Lines) {
heightTree.SetHeight(line, line.LineNumber);
heightTree.SetHeight(line.LineNumber, line.LineNumber);
}
}

Expand Down Expand Up @@ -82,7 +82,7 @@ public void FullCheck()
for (int i = 1; i <= 10; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(i));
}
CheckHeights();
CheckHeights();
} catch {
Console.WriteLine("from = " + from + ", to = " + to);
throw;
Expand Down
14 changes: 7 additions & 7 deletions ICSharpCode.AvalonEdit.Tests/Document/HeightTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void Setup()
document.Text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10";
heightTree = new HeightTree(document, 10);
foreach (DocumentLine line in document.Lines) {
heightTree.SetHeight(line, line.LineNumber);
heightTree.SetHeight(line.LineNumber, line.LineNumber);
}
}

Expand All @@ -56,17 +56,17 @@ public void TestLinesRemoved()
[Test]
public void TestHeightChanged()
{
heightTree.SetHeight(document.GetLineByNumber(4), 100);
heightTree.SetHeight(4, 100);
CheckHeights();
}

[Test]
public void TestLinesInserted()
{
document.Insert(0, "x\ny\n");
heightTree.SetHeight(document.Lines[0], 100);
heightTree.SetHeight(document.Lines[1], 1000);
heightTree.SetHeight(document.Lines[2], 10000);
heightTree.SetHeight(1, 100);
heightTree.SetHeight(2, 1000);
heightTree.SetHeight(3, 10000);
CheckHeights();
}

Expand All @@ -77,13 +77,13 @@ void CheckHeights()

internal static void CheckHeights(TextDocument document, HeightTree heightTree)
{
double[] heights = document.Lines.Select(l => heightTree.GetIsCollapsed(l.LineNumber) ? 0 : heightTree.GetHeight(l)).ToArray();
double[] heights = document.Lines.Select(l => heightTree.GetIsCollapsed(l.LineNumber) ? 0 : heightTree.GetHeight(l.LineNumber)).ToArray();
double[] visualPositions = new double[document.LineCount+1];
for (int i = 0; i < heights.Length; i++) {
visualPositions[i+1]=visualPositions[i]+heights[i];
}
foreach (DocumentLine ls in document.Lines) {
Assert.AreEqual(visualPositions[ls.LineNumber-1], heightTree.GetVisualPosition(ls));
Assert.AreEqual(visualPositions[ls.LineNumber-1], heightTree.GetVisualPosition(ls.LineNumber));
}
Assert.AreEqual(visualPositions[document.LineCount], heightTree.TotalHeight);
}
Expand Down
53 changes: 32 additions & 21 deletions ICSharpCode.AvalonEdit.Tests/Document/RandomizedLineManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;

using ICSharpCode.AvalonEdit.Rendering;

using NUnit.Framework;

namespace ICSharpCode.AvalonEdit.Document
Expand All @@ -31,21 +34,21 @@ public class RandomizedLineManagerTest
{
TextDocument document;
Random rnd;

[OneTimeSetUp]
public void FixtureSetup()
{
int seed = Environment.TickCount;
Console.WriteLine("RandomizedLineManagerTest Seed: " + seed);
rnd = new Random(seed);
}

[SetUp]
public void Setup()
{
document = new TextDocument();
}

[Test]
public void ShortReplacements()
{
Expand All @@ -58,12 +61,12 @@ public void ShortReplacements()
for (int j = 0; j < newTextLength; j++) {
buffer[j] = chars[rnd.Next(0, chars.Length)];
}

document.Replace(offset, length, new string(buffer, 0, newTextLength));
CheckLines();
}
}

[Test]
public void LargeReplacements()
{
Expand All @@ -76,15 +79,15 @@ public void LargeReplacements()
for (int j = 0; j < newTextLength; j++) {
buffer[j] = chars[rnd.Next(0, chars.Length)];
}

string newText = new string(buffer, 0, newTextLength);
string expectedText = document.Text.Remove(offset, length).Insert(offset, newText);
document.Replace(offset, length, newText);
Assert.AreEqual(expectedText, document.Text);
CheckLines();
}
}

void CheckLines()
{
string text = document.Text;
Expand All @@ -100,48 +103,56 @@ void CheckLines()
Assert.AreEqual(i - lineStart, line.Length);
i++; // consume \n
lineNumber++;
lineStart = i+1;
lineStart = i + 1;
} else if (c == '\r' || c == '\n') {
DocumentLine line = document.GetLineByNumber(lineNumber);
Assert.AreEqual(lineNumber, line.LineNumber);
Assert.AreEqual(1, line.DelimiterLength);
Assert.AreEqual(lineStart, line.Offset);
Assert.AreEqual(i - lineStart, line.Length);
lineNumber++;
lineStart = i+1;
lineStart = i + 1;
}
}
Assert.AreEqual(lineNumber, document.LineCount);
}

[Test]
public void CollapsingTest()
{
char[] chars = { 'a', 'b', '\r', '\n' };
char[] buffer = new char[20];
HeightTree heightTree = new HeightTree(document, 10);
List<CollapsedLineSection> collapsedSections = new List<CollapsedLineSection>();
for (int i = 0; i < 2500; i++) {
// Console.WriteLine("Iteration " + i);
// Console.WriteLine(heightTree.GetTreeAsString());
// foreach (CollapsedLineSection cs in collapsedSections) {
// Console.WriteLine(cs);
// }

switch (rnd.Next(0, 10)) {
for (int i = 0; i < 25000; i++) {
// Debug.WriteLine("Iteration " + i);
// Debug.WriteLine(heightTree.GetTreeAsString());
// foreach (CollapsedLineSection cs in collapsedSections) {
// Debug.WriteLine(cs);
// }

int command = rnd.Next(0, 10);
switch (command) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
int offset = rnd.Next(0, document.TextLength);
int length = rnd.Next(0, document.TextLength - offset);
int length;
if (command == 0) {
length = rnd.Next(0, document.TextLength - offset);
} else if (command == 1) {
length = 0;
} else {
length = rnd.Next(0, Math.Min(15, document.TextLength - offset));
}
int newTextLength = rnd.Next(0, 20);
for (int j = 0; j < newTextLength; j++) {
buffer[j] = chars[rnd.Next(0, chars.Length)];
}

document.Replace(offset, length, new string(buffer, 0, newTextLength));
break;
case 6:
Expand All @@ -162,7 +173,7 @@ public void CollapsingTest()
break;
case 9:
foreach (DocumentLine ls in document.Lines) {
heightTree.SetHeight(ls, ls.LineNumber);
heightTree.SetHeight(ls.LineNumber, ls.LineNumber);
}
break;
}
Expand Down
2 changes: 2 additions & 0 deletions ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<AssemblyVersion>6.1.0.0</AssemblyVersion>
<FileVersion>6.1.0.0</FileVersion>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<LangVersion>10</LangVersion>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -29,6 +30,7 @@
<PackageIcon>images\AvalonEditNuGetPackageIcon.png</PackageIcon>
<PackageTags>WPF Text Editor SharpDevelop AvalonEdit</PackageTags>
<PackageReleaseNotes>Changes are detailed at https://github.com/icsharpcode/AvalonEdit/blob/master/ChangeLog.md</PackageReleaseNotes>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0-windows' ">
Expand Down
76 changes: 63 additions & 13 deletions ICSharpCode.AvalonEdit/Rendering/CollapsedLineSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;

using ICSharpCode.AvalonEdit.Document;

Expand All @@ -26,8 +28,14 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </summary>
public sealed class CollapsedLineSection
{
DocumentLine start, end;
HeightTree heightTree;
// note: we don't need to store start/end, we could recompute them from
// the height tree if that had parent pointers.
DocumentLine? start, end;
internal readonly HeightTree heightTree;
internal HeightTreeLeafNode? startLeaf, endLeaf;
// tree nodes that contains the start/end of the collapsed section
internal byte startIndexInLeaf, endIndexInLeaf;
// start/end line within the HeightTreeLeafNode

#if DEBUG
internal string ID;
Expand Down Expand Up @@ -61,7 +69,7 @@ public bool IsCollapsed {
/// When the section is uncollapsed or the text containing it is deleted,
/// this property returns null.
/// </summary>
public DocumentLine Start {
public DocumentLine? Start {
get { return start; }
internal set { start = value; }
}
Expand All @@ -71,30 +79,46 @@ public DocumentLine Start {
/// When the section is uncollapsed or the text containing it is deleted,
/// this property returns null.
/// </summary>
public DocumentLine End {
public DocumentLine? End {
get { return end; }
internal set { end = value; }
}

internal void Reset()
{
start = end = null;
startLeaf = endLeaf = null;
}

/// <summary>
/// Uncollapses the section.
/// This causes the Start and End properties to be set to null!
/// Does nothing if the section is already uncollapsed.
/// </summary>
public void Uncollapse()
{
if (start == null)
if (startLeaf == null || endLeaf == null)
return;

if (!heightTree.IsDisposed) {
heightTree.Uncollapse(this);
#if DEBUG
heightTree.CheckProperties();
#endif
HeightTreeNode startNode = startLeaf;
HeightTreeNode endNode = endLeaf;
while (startNode != endNode) {
startNode.RemoveEvent(this, HeightTreeNode.EventKind.Start);
endNode.RemoveEvent(this, HeightTreeNode.EventKind.End);
startNode.parent!.UpdateHeight(startNode.indexInParent);
endNode.parent!.UpdateHeight(endNode.indexInParent);
startNode = startNode.parent;
endNode = endNode.parent;
}
// Now we have arrived at the node which has both events.
startNode.RemoveEvent(this, HeightTreeNode.EventKind.Start);
startNode.RemoveEvent(this, HeightTreeNode.EventKind.End);
// Propagate the new height up to the root node.
while (startNode.parent != null) {
startNode.parent.UpdateHeight(startNode.indexInParent);
startNode = startNode.parent;
}

start = null;
end = null;
Reset();
}

/// <summary>
Expand All @@ -106,5 +130,31 @@ public override string ToString()
return "[CollapsedSection" + ID + " Start=" + (start != null ? start.LineNumber.ToString() : "null")
+ " End=" + (end != null ? end.LineNumber.ToString() : "null") + "]";
}

internal bool StartIsWithin(HeightTreeNode heightTreeNode, out int index)
{
index = startIndexInLeaf;
HeightTreeNode? node = startLeaf;
while (node != null) {
if (node == heightTreeNode)
return true;
index = node.indexInParent;
node = node.parent;
}
return false;
}

internal bool EndIsWithin(HeightTreeNode heightTreeNode, out int index)
{
index = endIndexInLeaf;
HeightTreeNode? node = endLeaf;
while (node != null) {
if (node == heightTreeNode)
return true;
index = node.indexInParent;
node = node.parent;
}
return false;
}
}
}
Loading