Browse Source

Remove dictionary from HeightTree.

pull/15/head
Daniel Grunwald 15 years ago
parent
commit
27d20d4daf
  1. 30
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs
  2. 2
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/HeightTests.cs
  3. 23
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/HeightTree.cs
  4. 6
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

30
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/CollapsingTests.cs

@ -28,13 +28,13 @@ namespace ICSharpCode.AvalonEdit.Document
{ {
CollapsedLineSection sec1 = heightTree.CollapseText(document.GetLineByNumber(from), document.GetLineByNumber(to)); CollapsedLineSection sec1 = heightTree.CollapseText(document.GetLineByNumber(from), document.GetLineByNumber(to));
for (int i = 1; i < from; i++) { for (int i = 1; i < from; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
for (int i = from; i <= to; i++) { for (int i = from; i <= to; i++) {
Assert.IsTrue(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsTrue(heightTree.GetIsCollapsed(i));
} }
for (int i = to + 1; i <= 10; i++) { for (int i = to + 1; i <= 10; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
return sec1; return sec1;
@ -52,7 +52,7 @@ namespace ICSharpCode.AvalonEdit.Document
CollapsedLineSection sec1 = heightTree.CollapseText(document.GetLineByNumber(4), document.GetLineByNumber(6)); CollapsedLineSection sec1 = heightTree.CollapseText(document.GetLineByNumber(4), document.GetLineByNumber(6));
sec1.Uncollapse(); sec1.Uncollapse();
for (int i = 1; i <= 10; i++) { for (int i = 1; i <= 10; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
} }
@ -65,7 +65,7 @@ namespace ICSharpCode.AvalonEdit.Document
try { try {
SimpleCheck(from, to).Uncollapse(); SimpleCheck(from, to).Uncollapse();
for (int i = 1; i <= 10; i++) { for (int i = 1; i <= 10; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
} catch { } catch {
@ -82,13 +82,13 @@ namespace ICSharpCode.AvalonEdit.Document
CollapsedLineSection sec1 = heightTree.CollapseText(document.GetLineByNumber(4), document.GetLineByNumber(6)); CollapsedLineSection sec1 = heightTree.CollapseText(document.GetLineByNumber(4), document.GetLineByNumber(6));
document.Insert(document.GetLineByNumber(5).Offset, "a\nb\nc"); document.Insert(document.GetLineByNumber(5).Offset, "a\nb\nc");
for (int i = 1; i < 4; i++) { for (int i = 1; i < 4; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
for (int i = 4; i <= 8; i++) { for (int i = 4; i <= 8; i++) {
Assert.IsTrue(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsTrue(heightTree.GetIsCollapsed(i));
} }
for (int i = 9; i <= 12; i++) { for (int i = 9; i <= 12; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
} }
@ -101,13 +101,13 @@ namespace ICSharpCode.AvalonEdit.Document
int line6Offset = document.GetLineByNumber(6).Offset; int line6Offset = document.GetLineByNumber(6).Offset;
document.Remove(line4Offset, line6Offset - line4Offset); document.Remove(line4Offset, line6Offset - line4Offset);
for (int i = 1; i < 3; i++) { for (int i = 1; i < 3; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
for (int i = 3; i <= 5; i++) { for (int i = 3; i <= 5; i++) {
Assert.IsTrue(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsTrue(heightTree.GetIsCollapsed(i));
} }
for (int i = 6; i <= 8; i++) { for (int i = 6; i <= 8; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
} }
@ -120,13 +120,13 @@ namespace ICSharpCode.AvalonEdit.Document
int line8Offset = document.GetLineByNumber(8).Offset; int line8Offset = document.GetLineByNumber(8).Offset;
document.Remove(line5Offset, line8Offset - line5Offset); document.Remove(line5Offset, line8Offset - line5Offset);
for (int i = 1; i < 3; i++) { for (int i = 1; i < 3; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
for (int i = 3; i <= 5; i++) { for (int i = 3; i <= 5; i++) {
Assert.IsTrue(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsTrue(heightTree.GetIsCollapsed(i));
} }
for (int i = 6; i <= 7; i++) { for (int i = 6; i <= 7; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
} }
@ -138,7 +138,7 @@ namespace ICSharpCode.AvalonEdit.Document
int line3Offset = document.GetLineByNumber(3).Offset; int line3Offset = document.GetLineByNumber(3).Offset;
document.Remove(line3Offset - 1, 1); document.Remove(line3Offset - 1, 1);
for (int i = 1; i <= 9; i++) { for (int i = 1; i <= 9; i++) {
Assert.IsFalse(heightTree.GetIsCollapsed(document.GetLineByNumber(i))); Assert.IsFalse(heightTree.GetIsCollapsed(i));
} }
CheckHeights(); CheckHeights();
Assert.AreSame(null, sec1.Start); Assert.AreSame(null, sec1.Start);

2
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit.Tests/Document/HeightTests.cs

@ -62,7 +62,7 @@ namespace ICSharpCode.AvalonEdit.Document
internal static void CheckHeights(TextDocument document, HeightTree heightTree) internal static void CheckHeights(TextDocument document, HeightTree heightTree)
{ {
double[] heights = document.Lines.Select(l => heightTree.GetIsCollapsed(l) ? 0 : heightTree.GetHeight(l)).ToArray(); double[] heights = document.Lines.Select(l => heightTree.GetIsCollapsed(l.LineNumber) ? 0 : heightTree.GetHeight(l)).ToArray();
double[] visualPositions = new double[document.LineCount+1]; double[] visualPositions = new double[document.LineCount+1];
for (int i = 0; i < heights.Length; i++) { for (int i = 0; i < heights.Length; i++) {
visualPositions[i+1]=visualPositions[i]+heights[i]; visualPositions[i+1]=visualPositions[i]+heights[i];

23
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/HeightTree.cs

@ -17,8 +17,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
sealed class HeightTree : ILineTracker, IDisposable sealed class HeightTree : ILineTracker, IDisposable
{ {
// TODO: Optimize this. This tree takes alot of memory. // TODO: Optimize this. This tree takes alot of memory.
// (56 bytes for HeightTreeNode + ca. 25 bytes per node for the dictionary) // (56 bytes for HeightTreeNode
// We should try to get rid of the dictionary and find height nodes per index. // We should try to get rid of the dictionary and find height nodes per index. (DONE!)
// And we might do much better by compressing lines with the same height into a single node. // And we might do much better by compressing lines with the same height into a single node.
// That would also improve load times because we would always start with just a single node. // That would also improve load times because we would always start with just a single node.
@ -39,7 +39,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
#region Constructor #region Constructor
readonly TextDocument document; readonly TextDocument document;
Dictionary<DocumentLine, HeightTreeNode> dict;
HeightTreeNode root; HeightTreeNode root;
WeakLineTracker weakLineTracker; WeakLineTracker weakLineTracker;
@ -55,7 +54,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
{ {
if (weakLineTracker != null) if (weakLineTracker != null)
weakLineTracker.Deregister(); weakLineTracker.Deregister();
this.dict = null;
this.root = null; this.root = null;
this.weakLineTracker = null; this.weakLineTracker = null;
} }
@ -64,7 +62,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
HeightTreeNode GetNode(DocumentLine ls) HeightTreeNode GetNode(DocumentLine ls)
{ {
return dict[ls]; return GetNodeByIndex(ls.LineNumber - 1);
} }
#endregion #endregion
@ -83,13 +81,10 @@ namespace ICSharpCode.AvalonEdit.Rendering
s.End = null; s.End = null;
} }
dict = new Dictionary<DocumentLine, HeightTreeNode>((int)(document.LineCount / 0.7));
HeightTreeNode[] nodes = new HeightTreeNode[document.LineCount]; HeightTreeNode[] nodes = new HeightTreeNode[document.LineCount];
int lineNumber = 0; int lineNumber = 0;
foreach (DocumentLine ls in document.Lines) { foreach (DocumentLine ls in document.Lines) {
HeightTreeNode node = new HeightTreeNode(ls, DefaultLineHeight); nodes[lineNumber++] = new HeightTreeNode(ls, DefaultLineHeight);
dict.Add(ls, node);
nodes[lineNumber++] = node;
} }
Debug.Assert(nodes.Length > 0); Debug.Assert(nodes.Length > 0);
// now build the corresponding balanced tree // now build the corresponding balanced tree
@ -135,11 +130,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
cs.End = null; cs.End = null;
} else if (cs.Start == line) { } else if (cs.Start == line) {
Uncollapse(cs); Uncollapse(cs);
cs.Start = GetLineByNumber(line.LineNumber + 1); cs.Start = line.NextLine;
AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1); AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1);
} else if (cs.End == line) { } else if (cs.End == line) {
Uncollapse(cs); Uncollapse(cs);
cs.End = GetLineByNumber(line.LineNumber - 1); cs.End = line.PreviousLine;
AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1); AddCollapsedSection(cs, cs.End.LineNumber - cs.Start.LineNumber + 1);
} }
} }
@ -149,7 +144,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
// clear collapsedSections from removed line: prevent damage if removed line is in "nodesToCheckForMerging" // clear collapsedSections from removed line: prevent damage if removed line is in "nodesToCheckForMerging"
node.lineNode.collapsedSections = null; node.lineNode.collapsedSections = null;
EndRemoval(); EndRemoval();
dict.Remove(line);
} }
// void ILineTracker.AfterRemoveLine(DocumentLine line) // void ILineTracker.AfterRemoveLine(DocumentLine line)
@ -168,7 +162,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
HeightTreeNode InsertAfter(HeightTreeNode node, DocumentLine newLine) HeightTreeNode InsertAfter(HeightTreeNode node, DocumentLine newLine)
{ {
HeightTreeNode newNode = new HeightTreeNode(newLine, DefaultLineHeight); HeightTreeNode newNode = new HeightTreeNode(newLine, DefaultLineHeight);
dict.Add(newLine, newNode);
if (node.right == null) { if (node.right == null) {
if (node.lineNode.collapsedSections != null) { if (node.lineNode.collapsedSections != null) {
// we are inserting directly after node - so copy all collapsedSections // we are inserting directly after node - so copy all collapsedSections
@ -485,9 +478,9 @@ namespace ICSharpCode.AvalonEdit.Rendering
UpdateAfterChildrenChange(node); UpdateAfterChildrenChange(node);
} }
public bool GetIsCollapsed(DocumentLine line) public bool GetIsCollapsed(int lineNumber)
{ {
var node = GetNode(line); var node = GetNodeByIndex(lineNumber - 1);
return node.lineNode.IsDirectlyCollapsed || GetIsCollapedFromNode(node); return node.lineNode.IsDirectlyCollapsed || GetIsCollapedFromNode(node);
} }

6
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -564,7 +564,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties(); TextRunProperties globalTextRunProperties = CreateGlobalTextRunProperties();
VisualLineTextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties); VisualLineTextParagraphProperties paragraphProperties = CreateParagraphProperties(globalTextRunProperties);
while (heightTree.GetIsCollapsed(documentLine)) { while (heightTree.GetIsCollapsed(documentLine.LineNumber)) {
documentLine = documentLine.PreviousLine; documentLine = documentLine.PreviousLine;
} }
@ -823,7 +823,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
IVisualLineTransformer[] lineTransformersArray, IVisualLineTransformer[] lineTransformersArray,
Size availableSize) Size availableSize)
{ {
if (heightTree.GetIsCollapsed(documentLine)) if (heightTree.GetIsCollapsed(documentLine.LineNumber))
throw new InvalidOperationException("Trying to build visual line from collapsed line"); throw new InvalidOperationException("Trying to build visual line from collapsed line");
Debug.WriteLine("Building line " + documentLine.LineNumber); Debug.WriteLine("Building line " + documentLine.LineNumber);
@ -839,7 +839,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
#if DEBUG #if DEBUG
for (int i = visualLine.FirstDocumentLine.LineNumber + 1; i <= visualLine.LastDocumentLine.LineNumber; i++) { for (int i = visualLine.FirstDocumentLine.LineNumber + 1; i <= visualLine.LastDocumentLine.LineNumber; i++) {
if (!heightTree.GetIsCollapsed(document.GetLineByNumber(i))) if (!heightTree.GetIsCollapsed(i))
throw new InvalidOperationException("Line " + i + " was skipped by a VisualLineElementGenerator, but it is not collapsed."); throw new InvalidOperationException("Line " + i + " was skipped by a VisualLineElementGenerator, but it is not collapsed.");
} }
#endif #endif

Loading…
Cancel
Save