Add concept of 'sequence' to RenderTreeNode. This is in preparation for diffing.
This commit is contained in:
parent
b038117961
commit
a0b354e0eb
|
|
@ -22,18 +22,18 @@ namespace HostedInAspNet.Client
|
|||
{
|
||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.OpenElement("h1");
|
||||
builder.AddText("Hello from RenderTree");
|
||||
builder.OpenElement(0, "h1");
|
||||
builder.AddText(1, "Hello from RenderTree");
|
||||
builder.CloseElement();
|
||||
|
||||
builder.OpenElement("ul");
|
||||
builder.OpenElement(2, "ul");
|
||||
|
||||
builder.OpenElement("li");
|
||||
builder.AddText("First item");
|
||||
builder.OpenElement(3, "li");
|
||||
builder.AddText(4, "First item");
|
||||
builder.CloseElement();
|
||||
|
||||
builder.OpenElement("li");
|
||||
builder.AddText("Second item");
|
||||
builder.OpenElement(5, "li");
|
||||
builder.AddText(6, "Second item");
|
||||
builder.CloseElement();
|
||||
|
||||
builder.CloseElement();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { System_String, System_Array, Pointer } from '../Platform/Platform';
|
||||
import { platform } from '../Environment';
|
||||
const renderTreeNodeStructLength = 40;
|
||||
const renderTreeNodeStructLength = 44;
|
||||
|
||||
// To minimise GC pressure, instead of instantiating a JS object to represent each tree node,
|
||||
// we work in terms of pointers to the structs on the .NET heap, and use static functions that
|
||||
|
|
@ -12,15 +12,15 @@ export function getTreeNodePtr(renderTreeEntries: System_Array, index: number):
|
|||
|
||||
export const renderTreeNode = {
|
||||
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeNode.cs
|
||||
nodeType: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 0) as NodeType,
|
||||
elementName: (node: RenderTreeNodePointer) => platform.readStringField(node, 4),
|
||||
descendantsEndIndex: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 8) as NodeType,
|
||||
textContent: (node: RenderTreeNodePointer) => platform.readStringField(node, 12),
|
||||
attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 16),
|
||||
attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 20),
|
||||
attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 24),
|
||||
componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 32),
|
||||
component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 36),
|
||||
nodeType: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 4) as NodeType,
|
||||
elementName: (node: RenderTreeNodePointer) => platform.readStringField(node, 8),
|
||||
descendantsEndIndex: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 12) as NodeType,
|
||||
textContent: (node: RenderTreeNodePointer) => platform.readStringField(node, 16),
|
||||
attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 20),
|
||||
attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 24),
|
||||
attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 28),
|
||||
componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 36),
|
||||
component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 40),
|
||||
};
|
||||
|
||||
export enum NodeType {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
private IList<object> _currentAttributeValues;
|
||||
private IDictionary<string, object> _currentElementAttributes = new Dictionary<string, object>();
|
||||
private IList<IntermediateToken> _currentElementAttributeTokens = new List<IntermediateToken>();
|
||||
private int _sourceSequence = 0;
|
||||
|
||||
public override void BeginWriterScope(CodeRenderingContext context, string writer)
|
||||
{
|
||||
|
|
@ -106,7 +107,9 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
// Since we're not in the middle of writing an element, this must evaluate as some
|
||||
// text to display
|
||||
context.CodeWriter
|
||||
.WriteStartMethodInvocation($"{builderVarName}.{nameof(RenderTreeBuilder.AddText)}");
|
||||
.WriteStartMethodInvocation($"{builderVarName}.{nameof(RenderTreeBuilder.AddText)}")
|
||||
.Write((_sourceSequence++).ToString())
|
||||
.WriteParameterSeparator();
|
||||
|
||||
for (var i = 0; i < node.Children.Count; i++)
|
||||
{
|
||||
|
|
@ -187,6 +190,8 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
// Text node
|
||||
codeWriter
|
||||
.WriteStartMethodInvocation($"{builderVarName}.{nameof(RenderTreeBuilder.AddText)}")
|
||||
.Write((_sourceSequence++).ToString())
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(nextToken.Data)
|
||||
.WriteEndMethodInvocation();
|
||||
break;
|
||||
|
|
@ -200,6 +205,8 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
{
|
||||
codeWriter
|
||||
.WriteStartMethodInvocation($"{builderVarName}.{nameof(RenderTreeBuilder.OpenElement)}")
|
||||
.Write((_sourceSequence++).ToString())
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(nextTag.Data)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,11 +35,12 @@ namespace Microsoft.Blazor.RenderTree
|
|||
/// also call <see cref="CloseElement"/> immediately after appending the
|
||||
/// new element's child nodes.
|
||||
/// </summary>
|
||||
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
|
||||
/// <param name="elementName">A value representing the type of the element.</param>
|
||||
public void OpenElement(string elementName)
|
||||
public void OpenElement(int sequence, string elementName)
|
||||
{
|
||||
_openElementIndices.Push(_entriesInUse);
|
||||
Append(RenderTreeNode.Element(elementName));
|
||||
Append(RenderTreeNode.Element(sequence, elementName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -55,16 +56,18 @@ namespace Microsoft.Blazor.RenderTree
|
|||
/// <summary>
|
||||
/// Appends a node representing text content.
|
||||
/// </summary>
|
||||
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
|
||||
/// <param name="textContent">Content for the new text node.</param>
|
||||
public void AddText(string textContent)
|
||||
=> Append(RenderTreeNode.Text(textContent));
|
||||
public void AddText(int sequence, string textContent)
|
||||
=> Append(RenderTreeNode.Text(sequence, textContent));
|
||||
|
||||
/// <summary>
|
||||
/// Appends a node representing text content.
|
||||
/// </summary>
|
||||
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
|
||||
/// <param name="textContent">Content for the new text node.</param>
|
||||
public void AddText(object textContent)
|
||||
=> AddText(textContent?.ToString());
|
||||
public void AddText(int sequence, object textContent)
|
||||
=> AddText(sequence, textContent?.ToString());
|
||||
|
||||
/// <summary>
|
||||
/// Appends a node representing a string-valued attribute.
|
||||
|
|
@ -123,8 +126,9 @@ namespace Microsoft.Blazor.RenderTree
|
|||
/// Appends a node representing a child component.
|
||||
/// </summary>
|
||||
/// <typeparam name="TComponent">The type of the child component.</typeparam>
|
||||
public void AddComponent<TComponent>() where TComponent: IComponent
|
||||
=> Append(RenderTreeNode.ChildComponent<TComponent>());
|
||||
/// <param name="sequence">An integer that represents the position of the instruction in the source code.</param>
|
||||
public void AddComponent<TComponent>(int sequence) where TComponent: IComponent
|
||||
=> Append(RenderTreeNode.ChildComponent<TComponent>(sequence));
|
||||
|
||||
private void AssertCanAddAttribute()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ namespace Microsoft.Blazor.RenderTree
|
|||
/// </summary>
|
||||
public struct RenderTreeNode
|
||||
{
|
||||
/// <summary>
|
||||
/// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Element"/>,
|
||||
/// <see cref="RenderTreeNodeType.Text"/>, or <see cref="RenderTreeNodeType.Component"/>,
|
||||
/// gets the sequence number of the node. Sequence numbers indicate the relative source
|
||||
/// positions of the instructions that inserted the nodes. Sequence numbers are only
|
||||
/// comparable within the same sequence (typically, the same source method).
|
||||
/// </summary>
|
||||
public int Sequence { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Describes the type of this node.
|
||||
/// </summary>
|
||||
|
|
@ -75,14 +84,16 @@ namespace Microsoft.Blazor.RenderTree
|
|||
/// </summary>
|
||||
public IComponent Component { get; private set; }
|
||||
|
||||
internal static RenderTreeNode Element(string elementName) => new RenderTreeNode
|
||||
internal static RenderTreeNode Element(int sequence, string elementName) => new RenderTreeNode
|
||||
{
|
||||
Sequence = sequence,
|
||||
NodeType = RenderTreeNodeType.Element,
|
||||
ElementName = elementName,
|
||||
};
|
||||
|
||||
internal static RenderTreeNode Text(string textContent) => new RenderTreeNode
|
||||
internal static RenderTreeNode Text(int sequence, string textContent) => new RenderTreeNode
|
||||
{
|
||||
Sequence = sequence,
|
||||
NodeType = RenderTreeNodeType.Text,
|
||||
TextContent = textContent ?? string.Empty,
|
||||
};
|
||||
|
|
@ -101,8 +112,9 @@ namespace Microsoft.Blazor.RenderTree
|
|||
AttributeEventHandlerValue = value
|
||||
};
|
||||
|
||||
internal static RenderTreeNode ChildComponent<T>() where T: IComponent => new RenderTreeNode
|
||||
internal static RenderTreeNode ChildComponent<T>(int sequence) where T: IComponent => new RenderTreeNode
|
||||
{
|
||||
Sequence = sequence,
|
||||
NodeType = RenderTreeNodeType.Component,
|
||||
ComponentType = typeof(T)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ namespace Microsoft.Blazor.Test
|
|||
var nullString = (string)null;
|
||||
|
||||
// Act
|
||||
builder.AddText("First item");
|
||||
builder.AddText(nullString);
|
||||
builder.AddText("Second item");
|
||||
builder.AddText(0, "First item");
|
||||
builder.AddText(0, nullString);
|
||||
builder.AddText(0, "Second item");
|
||||
|
||||
// Assert
|
||||
var nodes = builder.GetNodes();
|
||||
|
|
@ -64,8 +64,8 @@ namespace Microsoft.Blazor.Test
|
|||
var nullObject = (object)null;
|
||||
|
||||
// Act
|
||||
builder.AddText(1234);
|
||||
builder.AddText(nullObject);
|
||||
builder.AddText(0, 1234);
|
||||
builder.AddText(0, nullObject);
|
||||
|
||||
// Assert
|
||||
var nodes = builder.GetNodes();
|
||||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.Blazor.Test
|
|||
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||
|
||||
// Act
|
||||
builder.OpenElement("my element");
|
||||
builder.OpenElement(0, "my element");
|
||||
|
||||
// Assert
|
||||
var node = builder.GetNodes().Single();
|
||||
|
|
@ -96,8 +96,8 @@ namespace Microsoft.Blazor.Test
|
|||
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||
|
||||
// Act
|
||||
builder.AddText("some node so that the element isn't at position zero");
|
||||
builder.OpenElement("my element");
|
||||
builder.AddText(0, "some node so that the element isn't at position zero");
|
||||
builder.OpenElement(0, "my element");
|
||||
builder.CloseElement();
|
||||
|
||||
// Assert
|
||||
|
|
@ -113,11 +113,11 @@ namespace Microsoft.Blazor.Test
|
|||
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||
|
||||
// Act
|
||||
builder.OpenElement("my element");
|
||||
builder.AddText("child 1");
|
||||
builder.AddText("child 2");
|
||||
builder.OpenElement(0, "my element");
|
||||
builder.AddText(0, "child 1");
|
||||
builder.AddText(0, "child 2");
|
||||
builder.CloseElement();
|
||||
builder.AddText("unrelated item");
|
||||
builder.AddText(0, "unrelated item");
|
||||
|
||||
// Assert
|
||||
var nodes = builder.GetNodes();
|
||||
|
|
@ -132,22 +132,22 @@ namespace Microsoft.Blazor.Test
|
|||
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||
|
||||
// Act
|
||||
builder.AddText("standalone text 1"); // 0: standalone text 1
|
||||
builder.OpenElement("root"); // 1: <root>
|
||||
builder.AddText("root text 1"); // 2: root text 1
|
||||
builder.AddText("root text 2"); // 3: root text 2
|
||||
builder.OpenElement("child"); // 4: <child>
|
||||
builder.AddText("child text"); // 5: child text
|
||||
builder.OpenElement("grandchild"); // 6: <grandchild>
|
||||
builder.AddText("grandchild text 1"); // 7: grandchild text 1
|
||||
builder.AddText("grandchild text 2"); // 8: grandchild text 2
|
||||
builder.CloseElement(); // </grandchild>
|
||||
builder.CloseElement(); // </child>
|
||||
builder.AddText("root text 3"); // 9: root text 3
|
||||
builder.OpenElement("child 2"); // 10: <child 2>
|
||||
builder.CloseElement(); // </child 2>
|
||||
builder.CloseElement(); // </root>
|
||||
builder.AddText("standalone text 2"); // 11: standalone text 2
|
||||
builder.AddText(0, "standalone text 1"); // 0: standalone text 1
|
||||
builder.OpenElement(0, "root"); // 1: <root>
|
||||
builder.AddText(0, "root text 1"); // 2: root text 1
|
||||
builder.AddText(0, "root text 2"); // 3: root text 2
|
||||
builder.OpenElement(0, "child"); // 4: <child>
|
||||
builder.AddText(0, "child text"); // 5: child text
|
||||
builder.OpenElement(0, "grandchild"); // 6: <grandchild>
|
||||
builder.AddText(0, "grandchild text 1"); // 7: grandchild text 1
|
||||
builder.AddText(0, "grandchild text 2"); // 8: grandchild text 2
|
||||
builder.CloseElement(); // </grandchild>
|
||||
builder.CloseElement(); // </child>
|
||||
builder.AddText(0, "root text 3"); // 9: root text 3
|
||||
builder.OpenElement(0, "child 2"); // 10: <child 2>
|
||||
builder.CloseElement(); // </child 2>
|
||||
builder.CloseElement(); // </root>
|
||||
builder.AddText(0, "standalone text 2"); // 11: standalone text 2
|
||||
|
||||
// Assert
|
||||
Assert.Collection(builder.GetNodes(),
|
||||
|
|
@ -173,12 +173,12 @@ namespace Microsoft.Blazor.Test
|
|||
UIEventHandler eventHandler = eventInfo => { };
|
||||
|
||||
// Act
|
||||
builder.OpenElement("myelement"); // 0: <myelement
|
||||
builder.OpenElement(0, "myelement"); // 0: <myelement
|
||||
builder.AddAttribute("attribute1", "value 1"); // 1: attribute1="value 1"
|
||||
builder.AddAttribute("attribute2", 123); // 2: attribute2=intExpression123>
|
||||
builder.OpenElement("child"); // 3: <child
|
||||
builder.OpenElement(0, "child"); // 3: <child
|
||||
builder.AddAttribute("childevent", eventHandler); // 4: childevent=eventHandler>
|
||||
builder.AddText("some text"); // 5: some text
|
||||
builder.AddText(0, "some text"); // 5: some text
|
||||
builder.CloseElement(); // </child>
|
||||
builder.CloseElement(); // </myelement>
|
||||
|
||||
|
|
@ -227,8 +227,8 @@ namespace Microsoft.Blazor.Test
|
|||
// Act/Assert
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
builder.OpenElement("some element");
|
||||
builder.AddText("hello");
|
||||
builder.OpenElement(0, "some element");
|
||||
builder.AddText(1, "hello");
|
||||
builder.AddAttribute("name", "value");
|
||||
});
|
||||
}
|
||||
|
|
@ -242,8 +242,8 @@ namespace Microsoft.Blazor.Test
|
|||
// Act/Assert
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
builder.OpenElement("some element");
|
||||
builder.AddText("hello");
|
||||
builder.OpenElement(0, "some element");
|
||||
builder.AddText(1, "hello");
|
||||
builder.AddAttribute("name", eventInfo => { });
|
||||
});
|
||||
}
|
||||
|
|
@ -255,11 +255,11 @@ namespace Microsoft.Blazor.Test
|
|||
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||
|
||||
// Act
|
||||
builder.OpenElement("parent"); // 0: <parent>
|
||||
builder.AddComponent<TestComponent>(); // 1: <testcomponent
|
||||
builder.OpenElement(0, "parent"); // 0: <parent>
|
||||
builder.AddComponent<TestComponent>(1); // 1: <testcomponent
|
||||
builder.AddAttribute("child1attribute1", "A"); // 2: child1attribute1="A"
|
||||
builder.AddAttribute("child1attribute2", "B"); // 3: child1attribute2="B" />
|
||||
builder.AddComponent<TestComponent>(); // 4: <testcomponent
|
||||
builder.AddComponent<TestComponent>(2); // 4: <testcomponent
|
||||
builder.AddAttribute("child2attribute", "C"); // 5: child2attribute="C" />
|
||||
builder.CloseElement(); // </parent>
|
||||
|
||||
|
|
@ -280,9 +280,9 @@ namespace Microsoft.Blazor.Test
|
|||
var builder = new RenderTreeBuilder(new TestRenderer());
|
||||
|
||||
// Act
|
||||
builder.AddText("some text");
|
||||
builder.OpenElement("elem");
|
||||
builder.AddText("more text");
|
||||
builder.AddText(0, "some text");
|
||||
builder.OpenElement(1, "elem");
|
||||
builder.AddText(2, "more text");
|
||||
builder.CloseElement();
|
||||
builder.Clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ namespace Microsoft.Blazor.Test
|
|||
var renderer = new TestRenderer();
|
||||
var component = new TestComponent(builder =>
|
||||
{
|
||||
builder.OpenElement("my element");
|
||||
builder.AddText("some text");
|
||||
builder.OpenElement(0, "my element");
|
||||
builder.AddText(1, "some text");
|
||||
builder.CloseElement();
|
||||
});
|
||||
|
||||
|
|
@ -43,8 +43,8 @@ namespace Microsoft.Blazor.Test
|
|||
var renderer = new TestRenderer();
|
||||
var component = new TestComponent(builder =>
|
||||
{
|
||||
builder.AddText("Hello");
|
||||
builder.AddComponent<MessageComponent>();
|
||||
builder.AddText(0, "Hello");
|
||||
builder.AddComponent<MessageComponent>(1);
|
||||
});
|
||||
|
||||
// Act/Assert
|
||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.Blazor.Test
|
|||
var renderer = new TestRenderer();
|
||||
var parentComponent = new TestComponent(builder =>
|
||||
{
|
||||
builder.AddComponent<MessageComponent>();
|
||||
builder.AddComponent<MessageComponent>(0);
|
||||
});
|
||||
var parentComponentId = renderer.AssignComponentId(parentComponent);
|
||||
renderer.RenderComponent(parentComponentId);
|
||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.Blazor.Test
|
|||
var renderer = new TestRenderer();
|
||||
var parentComponent = new TestComponent(builder =>
|
||||
{
|
||||
builder.AddComponent<EventComponent>();
|
||||
builder.AddComponent<EventComponent>(0);
|
||||
});
|
||||
var parentComponentId = renderer.AssignComponentId(parentComponent);
|
||||
renderer.RenderComponent(parentComponentId);
|
||||
|
|
@ -350,7 +350,7 @@ namespace Microsoft.Blazor.Test
|
|||
|
||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddText(Message);
|
||||
builder.AddText(0, Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,7 +360,7 @@ namespace Microsoft.Blazor.Test
|
|||
|
||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.OpenElement("some element");
|
||||
builder.OpenElement(0, "some element");
|
||||
builder.AddAttribute("some event", Handler);
|
||||
builder.CloseElement();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ namespace BasicTestApp
|
|||
{
|
||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.OpenElement("fieldset");
|
||||
builder.OpenElement("legend");
|
||||
builder.AddText("Parent component");
|
||||
builder.OpenElement(0, "fieldset");
|
||||
builder.OpenElement(1, "legend");
|
||||
builder.AddText(2, "Parent component");
|
||||
builder.CloseElement();
|
||||
builder.AddComponent<ChildComponent>();
|
||||
builder.AddComponent<ChildComponent>(3);
|
||||
builder.CloseElement();
|
||||
}
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ namespace BasicTestApp
|
|||
{
|
||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
builder.AddText("Child component");
|
||||
builder.AddText(0, "Child component");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue