In tree diffs, omit trailing Continue entries and skip over unmodified subtrees
This commit is contained in:
parent
b021e19598
commit
7c30d51be9
|
|
@ -9,7 +9,7 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
private const int MinBufferLength = 10;
|
private const int MinBufferLength = 10;
|
||||||
private RenderTreeDiffEntry[] _entries = new RenderTreeDiffEntry[10];
|
private RenderTreeDiffEntry[] _entries = new RenderTreeDiffEntry[10];
|
||||||
private int _entriesInUse = 0;
|
private int _entriesInUse;
|
||||||
|
|
||||||
public ArraySegment<RenderTreeDiffEntry> ComputeDifference(
|
public ArraySegment<RenderTreeDiffEntry> ComputeDifference(
|
||||||
ArraySegment<RenderTreeNode> oldTree,
|
ArraySegment<RenderTreeNode> oldTree,
|
||||||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
_entriesInUse = 0;
|
_entriesInUse = 0;
|
||||||
AppendDiffEntriesForRange(oldTree.Array, 0, oldTree.Count, newTree.Array, 0, newTree.Count);
|
AppendDiffEntriesForRange(oldTree.Array, 0, oldTree.Count, newTree.Array, 0, newTree.Count);
|
||||||
|
TrimTrailingContinueNodes();
|
||||||
|
|
||||||
// If the previous usage of the buffer showed that we have allocated
|
// If the previous usage of the buffer showed that we have allocated
|
||||||
// much more space than needed, free up the excess memory
|
// much more space than needed, free up the excess memory
|
||||||
|
|
@ -292,6 +293,19 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|
|
||||||
private void Append(RenderTreeDiffEntry entry)
|
private void Append(RenderTreeDiffEntry entry)
|
||||||
{
|
{
|
||||||
|
if (entry.Type == RenderTreeDiffEntryType.StepOut)
|
||||||
|
{
|
||||||
|
TrimTrailingContinueNodes();
|
||||||
|
|
||||||
|
// If the preceding node is now a StepIn, then we can coalesce the StepIn+StepOut
|
||||||
|
// down to a single Continue
|
||||||
|
if (_entriesInUse > 0 && _entries[_entriesInUse - 1].Type == RenderTreeDiffEntryType.StepIn)
|
||||||
|
{
|
||||||
|
_entriesInUse--;
|
||||||
|
entry = RenderTreeDiffEntry.Continue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_entriesInUse == _entries.Length)
|
if (_entriesInUse == _entries.Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref _entries, _entries.Length * 2);
|
Array.Resize(ref _entries, _entries.Length * 2);
|
||||||
|
|
@ -299,5 +313,13 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|
|
||||||
_entries[_entriesInUse++] = entry;
|
_entries[_entriesInUse++] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TrimTrailingContinueNodes()
|
||||||
|
{
|
||||||
|
while (_entriesInUse > 0 && _entries[_entriesInUse - 1].Type == RenderTreeDiffEntryType.Continue)
|
||||||
|
{
|
||||||
|
_entriesInUse--;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,7 @@ namespace Microsoft.Blazor.Test
|
||||||
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result,
|
Assert.Empty(result);
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<object[]> RecognizesEquivalentNodesAsSameCases()
|
public static IEnumerable<object[]> RecognizesEquivalentNodesAsSameCases()
|
||||||
|
|
@ -69,8 +68,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -92,8 +90,7 @@ namespace Microsoft.Blazor.Test
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result,
|
Assert.Collection(result,
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type));
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -117,8 +114,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result,
|
Assert.Collection(result,
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type));
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -150,8 +146,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -241,8 +236,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.PrependNode, entry.Type);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -266,8 +260,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result,
|
Assert.Collection(result,
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type),
|
entry => Assert.Equal(RenderTreeDiffEntryType.RemoveNode, entry.Type));
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -387,8 +380,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.SetAttribute, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.SetAttribute, entry.Type);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -415,8 +407,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.RemoveAttribute, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.RemoveAttribute, entry.Type);
|
||||||
Assert.Equal("will be removed", entry.RemovedAttributeName);
|
Assert.Equal("will be removed", entry.RemovedAttributeName);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -444,8 +435,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.SetAttribute, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.SetAttribute, entry.Type);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -476,8 +466,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.SetAttribute, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.SetAttribute, entry.Type);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -508,8 +497,7 @@ namespace Microsoft.Blazor.Test
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeDiffEntryType.RemoveAttribute, entry.Type);
|
Assert.Equal(RenderTreeDiffEntryType.RemoveAttribute, entry.Type);
|
||||||
Assert.Equal("oldname", entry.RemovedAttributeName);
|
Assert.Equal("oldname", entry.RemovedAttributeName);
|
||||||
},
|
});
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -553,6 +541,74 @@ namespace Microsoft.Blazor.Test
|
||||||
entry => Assert.Equal(RenderTreeDiffEntryType.StepOut, entry.Type));
|
entry => Assert.Equal(RenderTreeDiffEntryType.StepOut, entry.Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SkipsUnmodifiedSubtrees()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||||
|
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||||
|
var diff = new RenderTreeDiff();
|
||||||
|
oldTree.OpenElement(10, "root");
|
||||||
|
oldTree.AddText(11, "Text that will change");
|
||||||
|
oldTree.OpenElement(12, "Subtree that will not change");
|
||||||
|
oldTree.OpenElement(13, "Another");
|
||||||
|
oldTree.AddText(14, "Text that will not change");
|
||||||
|
oldTree.CloseElement();
|
||||||
|
oldTree.CloseElement();
|
||||||
|
oldTree.CloseElement();
|
||||||
|
|
||||||
|
newTree.OpenElement(10, "root");
|
||||||
|
newTree.AddText(11, "Text that has changed");
|
||||||
|
newTree.OpenElement(12, "Subtree that will not change");
|
||||||
|
newTree.OpenElement(13, "Another");
|
||||||
|
newTree.AddText(14, "Text that will not change");
|
||||||
|
newTree.CloseElement();
|
||||||
|
newTree.CloseElement();
|
||||||
|
newTree.CloseElement();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(result,
|
||||||
|
entry => Assert.Equal(RenderTreeDiffEntryType.StepIn, entry.Type),
|
||||||
|
entry =>
|
||||||
|
{
|
||||||
|
Assert.Equal(RenderTreeDiffEntryType.UpdateText, entry.Type);
|
||||||
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
|
},
|
||||||
|
entry => Assert.Equal(RenderTreeDiffEntryType.StepOut, entry.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SkipsUnmodifiedTrailingSiblings()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||||
|
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||||
|
var diff = new RenderTreeDiff();
|
||||||
|
oldTree.AddText(10, "text1");
|
||||||
|
oldTree.AddText(11, "text2");
|
||||||
|
oldTree.AddText(12, "text3");
|
||||||
|
oldTree.AddText(13, "text4");
|
||||||
|
newTree.AddText(10, "text1");
|
||||||
|
newTree.AddText(11, "text2modified");
|
||||||
|
newTree.AddText(12, "text3");
|
||||||
|
newTree.AddText(13, "text4");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = diff.ComputeDifference(oldTree.GetNodes(), newTree.GetNodes());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(result,
|
||||||
|
entry => Assert.Equal(RenderTreeDiffEntryType.Continue, entry.Type),
|
||||||
|
entry =>
|
||||||
|
{
|
||||||
|
Assert.Equal(RenderTreeDiffEntryType.UpdateText, entry.Type);
|
||||||
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private class FakeRenderer : Renderer
|
private class FakeRenderer : Renderer
|
||||||
{
|
{
|
||||||
internal protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
|
internal protected override void UpdateDisplay(int componentId, ArraySegment<RenderTreeNode> renderTree)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue