Add the concept of a "Region" render tree frame
This commit is contained in:
parent
043e623d5b
commit
a9822216f1
|
|
@ -57,17 +57,6 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
|
entry = entry.WithElementSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Marks a previously appended component frame as closed. Calls to this method
|
|
||||||
/// must be balanced with calls to <see cref="OpenComponent{TComponent}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public void CloseComponent()
|
|
||||||
{
|
|
||||||
var indexOfEntryBeingClosed = _openElementIndices.Pop();
|
|
||||||
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
|
|
||||||
entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Appends a frame representing text content.
|
/// Appends a frame representing text content.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -171,6 +160,39 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
Append(RenderTreeFrame.ChildComponent<TComponent>(sequence));
|
Append(RenderTreeFrame.ChildComponent<TComponent>(sequence));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks a previously appended component frame as closed. Calls to this method
|
||||||
|
/// must be balanced with calls to <see cref="OpenComponent{TComponent}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void CloseComponent()
|
||||||
|
{
|
||||||
|
var indexOfEntryBeingClosed = _openElementIndices.Pop();
|
||||||
|
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
|
||||||
|
entry = entry.WithComponentSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends a frame denoting the start of a region (that is, a tree fragment that is
|
||||||
|
/// processed as a unit for the purposes of diffing).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
|
||||||
|
public void OpenRegion(int sequence)
|
||||||
|
{
|
||||||
|
_openElementIndices.Push(_entries.Count);
|
||||||
|
Append(RenderTreeFrame.Region(sequence));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks a previously appended region frame as closed. Calls to this method
|
||||||
|
/// must be balanced with calls to <see cref="OpenRegion"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal void CloseRegion()
|
||||||
|
{
|
||||||
|
var indexOfEntryBeingClosed = _openElementIndices.Pop();
|
||||||
|
ref var entry = ref _entries.Buffer[indexOfEntryBeingClosed];
|
||||||
|
entry = entry.WithRegionSubtreeLength(_entries.Count - indexOfEntryBeingClosed);
|
||||||
|
}
|
||||||
|
|
||||||
private void AssertCanAddAttribute()
|
private void AssertCanAddAttribute()
|
||||||
{
|
{
|
||||||
if (_lastNonAttributeFrameType != RenderTreeFrameType.Element
|
if (_lastNonAttributeFrameType != RenderTreeFrameType.Element
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,17 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[FieldOffset(24)] public readonly IComponent Component;
|
[FieldOffset(24)] public readonly IComponent Component;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
// RenderTreeFrameType.Region
|
||||||
|
// --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Region"/>
|
||||||
|
/// gets the number of frames in the subtree for which this frame is the root.
|
||||||
|
/// The value is zero if the frame has not yet been closed.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(8)] public readonly int RegionSubtreeLength;
|
||||||
|
|
||||||
private RenderTreeFrame(int sequence, string elementName, int elementSubtreeLength)
|
private RenderTreeFrame(int sequence, string elementName, int elementSubtreeLength)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
|
|
@ -170,6 +181,14 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
AttributeEventHandlerId = eventHandlerId;
|
AttributeEventHandlerId = eventHandlerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RenderTreeFrame(int sequence, int regionSubtreeLength)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
FrameType = RenderTreeFrameType.Region;
|
||||||
|
Sequence = sequence;
|
||||||
|
RegionSubtreeLength = regionSubtreeLength;
|
||||||
|
}
|
||||||
|
|
||||||
internal static RenderTreeFrame Element(int sequence, string elementName)
|
internal static RenderTreeFrame Element(int sequence, string elementName)
|
||||||
=> new RenderTreeFrame(sequence, elementName: elementName, elementSubtreeLength: 0);
|
=> new RenderTreeFrame(sequence, elementName: elementName, elementSubtreeLength: 0);
|
||||||
|
|
||||||
|
|
@ -185,6 +204,9 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
internal static RenderTreeFrame ChildComponent<T>(int sequence) where T : IComponent
|
internal static RenderTreeFrame ChildComponent<T>(int sequence) where T : IComponent
|
||||||
=> new RenderTreeFrame(sequence, typeof(T), 0);
|
=> new RenderTreeFrame(sequence, typeof(T), 0);
|
||||||
|
|
||||||
|
internal static RenderTreeFrame Region(int sequence)
|
||||||
|
=> new RenderTreeFrame(sequence, regionSubtreeLength: 0);
|
||||||
|
|
||||||
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
|
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
|
||||||
=> new RenderTreeFrame(Sequence, elementName: ElementName, elementSubtreeLength: elementSubtreeLength);
|
=> new RenderTreeFrame(Sequence, elementName: ElementName, elementSubtreeLength: elementSubtreeLength);
|
||||||
|
|
||||||
|
|
@ -199,5 +221,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
|
|
||||||
internal RenderTreeFrame WithAttributeEventHandlerId(int eventHandlerId)
|
internal RenderTreeFrame WithAttributeEventHandlerId(int eventHandlerId)
|
||||||
=> new RenderTreeFrame(Sequence, AttributeName, AttributeValue, eventHandlerId);
|
=> new RenderTreeFrame(Sequence, AttributeName, AttributeValue, eventHandlerId);
|
||||||
|
|
||||||
|
internal RenderTreeFrame WithRegionSubtreeLength(int regionSubtreeLength)
|
||||||
|
=> new RenderTreeFrame(Sequence, regionSubtreeLength: regionSubtreeLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,13 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
/// Represents a child component.
|
/// Represents a child component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Component = 4,
|
Component = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the boundary around range of sibling frames that should be treated as an
|
||||||
|
/// unsplittable group for the purposes of diffing. This is typically used when appending
|
||||||
|
/// a tree fragment generated by external code, because the sequence numbers in that tree
|
||||||
|
/// fragment are not comparable to sequence numbers outside it.
|
||||||
|
/// </summary>
|
||||||
|
Region = 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,20 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CannotAddAttributeToRegion()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||||
|
|
||||||
|
// Act/Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
{
|
||||||
|
builder.OpenRegion(0);
|
||||||
|
builder.AddAttribute(1, "name", "value");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanAddChildComponents()
|
public void CanAddChildComponents()
|
||||||
{
|
{
|
||||||
|
|
@ -272,6 +286,34 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
frame => AssertFrame.Attribute(frame, "child2attribute", "C"));
|
frame => AssertFrame.Attribute(frame, "child2attribute", "C"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanAddRegions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
builder.OpenElement(10, "parent"); // 0: <parent>
|
||||||
|
builder.OpenRegion(11); // 1: [region
|
||||||
|
builder.AddText(3, "Hello"); // 2: Hello
|
||||||
|
builder.OpenRegion(4); // 3: [region
|
||||||
|
builder.OpenElement(3, "another"); // 4: <another>
|
||||||
|
builder.CloseElement(); // </another>
|
||||||
|
builder.CloseRegion(); // ]
|
||||||
|
builder.AddText(6, "Goodbye"); // 5: Goodbye
|
||||||
|
builder.CloseRegion(); // ]
|
||||||
|
builder.CloseElement(); // </parent>
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(builder.GetFrames(),
|
||||||
|
frame => AssertFrame.Element(frame, "parent", 6, 10),
|
||||||
|
frame => AssertFrame.Region(frame, 5, 11),
|
||||||
|
frame => AssertFrame.Text(frame, "Hello", 3),
|
||||||
|
frame => AssertFrame.Region(frame, 2, 4),
|
||||||
|
frame => AssertFrame.Element(frame, "another", 1, 3),
|
||||||
|
frame => AssertFrame.Text(frame, "Goodbye", 6));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanClear()
|
public void CanClear()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,13 @@ namespace Microsoft.AspNetCore.Blazor.Test.Shared
|
||||||
Assert.Equal(componentId, frame.ComponentId);
|
Assert.Equal(componentId, frame.ComponentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Region(RenderTreeFrame frame, int subtreeLength, int? sequence = null)
|
||||||
|
{
|
||||||
|
Assert.Equal(RenderTreeFrameType.Region, frame.FrameType);
|
||||||
|
Assert.Equal(subtreeLength, frame.RegionSubtreeLength);
|
||||||
|
AssertFrame.Sequence(frame, sequence);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Whitespace(RenderTreeFrame frame, int? sequence = null)
|
public static void Whitespace(RenderTreeFrame frame, int? sequence = null)
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeFrameType.Text, frame.FrameType);
|
Assert.Equal(RenderTreeFrameType.Text, frame.FrameType);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue