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);
|
||||
}
|
||||
|
||||
/// <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 representing text content.
|
||||
/// </summary>
|
||||
|
|
@ -171,6 +160,39 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
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()
|
||||
{
|
||||
if (_lastNonAttributeFrameType != RenderTreeFrameType.Element
|
||||
|
|
|
|||
|
|
@ -118,6 +118,17 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
/// </summary>
|
||||
[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)
|
||||
: this()
|
||||
{
|
||||
|
|
@ -170,6 +181,14 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
AttributeEventHandlerId = eventHandlerId;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, int regionSubtreeLength)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Region;
|
||||
Sequence = sequence;
|
||||
RegionSubtreeLength = regionSubtreeLength;
|
||||
}
|
||||
|
||||
internal static RenderTreeFrame Element(int sequence, string elementName)
|
||||
=> 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
|
||||
=> new RenderTreeFrame(sequence, typeof(T), 0);
|
||||
|
||||
internal static RenderTreeFrame Region(int sequence)
|
||||
=> new RenderTreeFrame(sequence, regionSubtreeLength: 0);
|
||||
|
||||
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
|
||||
=> new RenderTreeFrame(Sequence, elementName: ElementName, elementSubtreeLength: elementSubtreeLength);
|
||||
|
||||
|
|
@ -199,5 +221,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
|||
|
||||
internal RenderTreeFrame WithAttributeEventHandlerId(int 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.
|
||||
/// </summary>
|
||||
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]
|
||||
public void CanAddChildComponents()
|
||||
{
|
||||
|
|
@ -272,6 +286,34 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
|||
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]
|
||||
public void CanClear()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,6 +67,13 @@ namespace Microsoft.AspNetCore.Blazor.Test.Shared
|
|||
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)
|
||||
{
|
||||
Assert.Equal(RenderTreeFrameType.Text, frame.FrameType);
|
||||
|
|
|
|||
Loading…
Reference in New Issue