Make RenderTreeEdit specify which sibling node it acts on, so we can eliminate the Continue concept and no longer track indices on JS side
This commit is contained in:
parent
0ea4c930e9
commit
df321b9b8d
|
|
@ -26,56 +26,58 @@ export class BrowserRenderer {
|
||||||
throw new Error(`No element is currently associated with component ${componentId}`);
|
throw new Error(`No element is currently associated with component ${componentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.applyEdits(componentId, { parent: element, childIndex: 0 }, edits, editsLength, referenceTree);
|
this.applyEdits(componentId, element, 0, edits, editsLength, referenceTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyEdits(componentId: number, location: DOMLocation, edits: System_Array, editsLength: number, referenceTree: System_Array) {
|
applyEdits(componentId: number, parent: Element, childIndex: number, edits: System_Array, editsLength: number, referenceTree: System_Array) {
|
||||||
|
const childIndexStack: number[] = []; // TODO: This can be removed. We only (potentially) have nonzero childIndex values at the root, so we only need to track the current depth to determine whether we are at the root
|
||||||
for (let editIndex = 0; editIndex < editsLength; editIndex++) {
|
for (let editIndex = 0; editIndex < editsLength; editIndex++) {
|
||||||
const edit = getRenderTreeEditPtr(edits, editIndex);
|
const edit = getRenderTreeEditPtr(edits, editIndex);
|
||||||
const editType = renderTreeEdit.type(edit);
|
const editType = renderTreeEdit.type(edit);
|
||||||
switch (editType) {
|
switch (editType) {
|
||||||
case EditType.continue: {
|
|
||||||
location.childIndex++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EditType.prependNode: {
|
case EditType.prependNode: {
|
||||||
const nodeIndex = renderTreeEdit.newTreeIndex(edit);
|
const nodeIndex = renderTreeEdit.newTreeIndex(edit);
|
||||||
const node = getTreeNodePtr(referenceTree, nodeIndex);
|
const node = getTreeNodePtr(referenceTree, nodeIndex);
|
||||||
this.insertNode(componentId, location, referenceTree, node, nodeIndex);
|
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
||||||
|
this.insertNode(componentId, parent, childIndex + siblingIndex, referenceTree, node, nodeIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.removeNode: {
|
case EditType.removeNode: {
|
||||||
removeNodeFromDOM(location);
|
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
||||||
|
removeNodeFromDOM(parent, childIndex + siblingIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.setAttribute: {
|
case EditType.setAttribute: {
|
||||||
const nodeIndex = renderTreeEdit.newTreeIndex(edit);
|
const nodeIndex = renderTreeEdit.newTreeIndex(edit);
|
||||||
const node = getTreeNodePtr(referenceTree, nodeIndex);
|
const node = getTreeNodePtr(referenceTree, nodeIndex);
|
||||||
const element = location.parent.childNodes[location.childIndex] as HTMLElement;
|
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
||||||
|
const element = parent.childNodes[childIndex + siblingIndex] as HTMLElement;
|
||||||
this.applyAttribute(componentId, element, node, nodeIndex);
|
this.applyAttribute(componentId, element, node, nodeIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.removeAttribute: {
|
case EditType.removeAttribute: {
|
||||||
removeAttributeFromDOM(location, renderTreeEdit.removedAttributeName(edit)!);
|
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
||||||
|
removeAttributeFromDOM(parent, childIndex + siblingIndex, renderTreeEdit.removedAttributeName(edit)!);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.updateText: {
|
case EditType.updateText: {
|
||||||
const nodeIndex = renderTreeEdit.newTreeIndex(edit);
|
const nodeIndex = renderTreeEdit.newTreeIndex(edit);
|
||||||
const node = getTreeNodePtr(referenceTree, nodeIndex);
|
const node = getTreeNodePtr(referenceTree, nodeIndex);
|
||||||
const domTextNode = location.parent.childNodes[location.childIndex] as Text;
|
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
||||||
|
const domTextNode = parent.childNodes[childIndex + siblingIndex] as Text;
|
||||||
domTextNode.textContent = renderTreeNode.textContent(node);
|
domTextNode.textContent = renderTreeNode.textContent(node);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.stepIn: {
|
case EditType.stepIn: {
|
||||||
location.parent = location.parent.childNodes[location.childIndex] as HTMLElement;
|
childIndexStack.push(childIndex);
|
||||||
location.childIndex = 0;
|
const siblingIndex = renderTreeEdit.siblingIndex(edit);
|
||||||
|
parent = parent.childNodes[childIndex + siblingIndex] as HTMLElement;
|
||||||
|
childIndex = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EditType.stepOut: {
|
case EditType.stepOut: {
|
||||||
// To avoid the indexOf, consider maintaining a stack of locations
|
parent = parent.parentElement!;
|
||||||
const targetElement = location.parent;
|
childIndex = childIndexStack.pop()!;
|
||||||
location.parent = location.parent.parentElement!;
|
|
||||||
location.childIndex = Array.prototype.indexOf.call(location.parent.childNodes, targetElement) + 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
@ -86,19 +88,19 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertNode(componentId: number, location: DOMLocation, nodes: System_Array, node: RenderTreeNodePointer, nodeIndex: number) {
|
insertNode(componentId: number, parent: Element, childIndex: number, nodes: System_Array, node: RenderTreeNodePointer, nodeIndex: number) {
|
||||||
const nodeType = renderTreeNode.nodeType(node);
|
const nodeType = renderTreeNode.nodeType(node);
|
||||||
switch (nodeType) {
|
switch (nodeType) {
|
||||||
case NodeType.element:
|
case NodeType.element:
|
||||||
this.insertElement(componentId, location, nodes, node, nodeIndex);
|
this.insertElement(componentId, parent, childIndex, nodes, node, nodeIndex);
|
||||||
break;
|
break;
|
||||||
case NodeType.text:
|
case NodeType.text:
|
||||||
this.insertText(location, node);
|
this.insertText(parent, childIndex, node);
|
||||||
break;
|
break;
|
||||||
case NodeType.attribute:
|
case NodeType.attribute:
|
||||||
throw new Error('Attribute nodes should only be present as leading children of element nodes.');
|
throw new Error('Attribute nodes should only be present as leading children of element nodes.');
|
||||||
case NodeType.component:
|
case NodeType.component:
|
||||||
this.insertComponent(location, node);
|
this.insertComponent(parent, childIndex, node);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
const unknownType: never = nodeType; // Compile-time verification that the switch was exhaustive
|
const unknownType: never = nodeType; // Compile-time verification that the switch was exhaustive
|
||||||
|
|
@ -106,10 +108,10 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertElement(componentId: number, location: DOMLocation, nodes: System_Array, node: RenderTreeNodePointer, nodeIndex: number) {
|
insertElement(componentId: number, parent: Element, childIndex: number, nodes: System_Array, node: RenderTreeNodePointer, nodeIndex: number) {
|
||||||
const tagName = renderTreeNode.elementName(node)!;
|
const tagName = renderTreeNode.elementName(node)!;
|
||||||
const newDomElement = document.createElement(tagName);
|
const newDomElement = document.createElement(tagName);
|
||||||
insertNodeIntoDOM(newDomElement, location);
|
insertNodeIntoDOM(newDomElement, parent, childIndex);
|
||||||
|
|
||||||
// Apply attributes
|
// Apply attributes
|
||||||
const descendantsEndIndex = renderTreeNode.descendantsEndIndex(node);
|
const descendantsEndIndex = renderTreeNode.descendantsEndIndex(node);
|
||||||
|
|
@ -120,15 +122,15 @@ export class BrowserRenderer {
|
||||||
} else {
|
} else {
|
||||||
// As soon as we see a non-attribute child, all the subsequent child nodes are
|
// As soon as we see a non-attribute child, all the subsequent child nodes are
|
||||||
// not attributes, so bail out and insert the remnants recursively
|
// not attributes, so bail out and insert the remnants recursively
|
||||||
this.insertNodeRange(componentId, { parent: newDomElement, childIndex: 0 }, nodes, descendantIndex, descendantsEndIndex);
|
this.insertNodeRange(componentId, newDomElement, 0, nodes, descendantIndex, descendantsEndIndex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertComponent(location: DOMLocation, node: RenderTreeNodePointer) {
|
insertComponent(parent: Element, childIndex: number, node: RenderTreeNodePointer) {
|
||||||
const containerElement = document.createElement('blazor-component');
|
const containerElement = document.createElement('blazor-component');
|
||||||
insertNodeIntoDOM(containerElement, location);
|
insertNodeIntoDOM(containerElement, parent, childIndex);
|
||||||
|
|
||||||
const childComponentId = renderTreeNode.componentId(node);
|
const childComponentId = renderTreeNode.componentId(node);
|
||||||
this.attachComponentToElement(childComponentId, containerElement);
|
this.attachComponentToElement(childComponentId, containerElement);
|
||||||
|
|
@ -146,10 +148,10 @@ export class BrowserRenderer {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
insertText(location: DOMLocation, textNode: RenderTreeNodePointer) {
|
insertText(parent: Element, childIndex: number, textNode: RenderTreeNodePointer) {
|
||||||
const textContent = renderTreeNode.textContent(textNode)!;
|
const textContent = renderTreeNode.textContent(textNode)!;
|
||||||
const newDomTextNode = document.createTextNode(textContent);
|
const newDomTextNode = document.createTextNode(textContent);
|
||||||
insertNodeIntoDOM(newDomTextNode, location);
|
insertNodeIntoDOM(newDomTextNode, parent, childIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyAttribute(componentId: number, toDomElement: Element, attributeNode: RenderTreeNodePointer, attributeNodeIndex: number) {
|
applyAttribute(componentId: number, toDomElement: Element, attributeNode: RenderTreeNodePointer, attributeNodeIndex: number) {
|
||||||
|
|
@ -189,10 +191,11 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertNodeRange(componentId: number, location: DOMLocation, nodes: System_Array, startIndex: number, endIndex: number) {
|
insertNodeRange(componentId: number, parent: Element, childIndex: number, nodes: System_Array, startIndex: number, endIndex: number) {
|
||||||
for (let index = startIndex; index <= endIndex; index++) {
|
for (let index = startIndex; index <= endIndex; index++) {
|
||||||
const node = getTreeNodePtr(nodes, index);
|
const node = getTreeNodePtr(nodes, index);
|
||||||
this.insertNode(componentId, location, nodes, node, index);
|
this.insertNode(componentId, parent, childIndex, nodes, node, index);
|
||||||
|
childIndex++;
|
||||||
|
|
||||||
// Skip over any descendants, since they are already dealt with recursively
|
// Skip over any descendants, since they are already dealt with recursively
|
||||||
const descendantsEndIndex = renderTreeNode.descendantsEndIndex(node);
|
const descendantsEndIndex = renderTreeNode.descendantsEndIndex(node);
|
||||||
|
|
@ -203,28 +206,20 @@ export class BrowserRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DOMLocation {
|
function insertNodeIntoDOM(node: Node, parent: Element, childIndex: number) {
|
||||||
parent: Element;
|
if (childIndex >= parent.childNodes.length) {
|
||||||
childIndex: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertNodeIntoDOM(node: Node, location: DOMLocation) {
|
|
||||||
const parent = location.parent;
|
|
||||||
if (location.childIndex >= parent.childNodes.length) {
|
|
||||||
parent.appendChild(node);
|
parent.appendChild(node);
|
||||||
} else {
|
} else {
|
||||||
parent.insertBefore(node, parent.childNodes[location.childIndex]);
|
parent.insertBefore(node, parent.childNodes[childIndex]);
|
||||||
}
|
}
|
||||||
location.childIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeNodeFromDOM(location: DOMLocation) {
|
function removeNodeFromDOM(parent: Element, childIndex: number) {
|
||||||
const parent = location.parent;
|
parent.removeChild(parent.childNodes[childIndex]);
|
||||||
parent.removeChild(parent.childNodes[location.childIndex]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeAttributeFromDOM(location: DOMLocation, attributeName: string) {
|
function removeAttributeFromDOM(parent: Element, childIndex: number, attributeName: string) {
|
||||||
const element = location.parent.childNodes[location.childIndex] as HTMLElement;
|
const element = parent.childNodes[childIndex] as Element;
|
||||||
element.removeAttribute(attributeName);
|
element.removeAttribute(attributeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { System_Array, Pointer } from '../Platform/Platform';
|
import { System_Array, Pointer } from '../Platform/Platform';
|
||||||
import { platform } from '../Environment';
|
import { platform } from '../Environment';
|
||||||
const renderTreeEditStructLength = 12;
|
const renderTreeEditStructLength = 16;
|
||||||
|
|
||||||
export function getRenderTreeEditPtr(renderTreeEdits: System_Array, index: number): RenderTreeEditPointer {
|
export function getRenderTreeEditPtr(renderTreeEdits: System_Array, index: number): RenderTreeEditPointer {
|
||||||
return platform.getArrayEntryPtr(renderTreeEdits, index, renderTreeEditStructLength) as RenderTreeEditPointer;
|
return platform.getArrayEntryPtr(renderTreeEdits, index, renderTreeEditStructLength) as RenderTreeEditPointer;
|
||||||
|
|
@ -9,19 +9,19 @@ export function getRenderTreeEditPtr(renderTreeEdits: System_Array, index: numbe
|
||||||
export const renderTreeEdit = {
|
export const renderTreeEdit = {
|
||||||
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeEdit.cs
|
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeEdit.cs
|
||||||
type: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 0) as EditType,
|
type: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 0) as EditType,
|
||||||
newTreeIndex: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 4),
|
siblingIndex: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 4),
|
||||||
removedAttributeName: (edit: RenderTreeEditPointer) => platform.readStringField(edit, 8),
|
newTreeIndex: (edit: RenderTreeEditPointer) => platform.readInt32Field(edit, 8),
|
||||||
|
removedAttributeName: (edit: RenderTreeEditPointer) => platform.readStringField(edit, 12),
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum EditType {
|
export enum EditType {
|
||||||
continue = 1,
|
prependNode = 1,
|
||||||
prependNode = 2,
|
removeNode = 2,
|
||||||
removeNode = 3,
|
setAttribute = 3,
|
||||||
setAttribute = 4,
|
removeAttribute = 4,
|
||||||
removeAttribute = 5,
|
updateText = 5,
|
||||||
updateText = 6,
|
stepIn = 6,
|
||||||
stepIn = 7,
|
stepOut = 7,
|
||||||
stepOut = 8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nominal type to ensure only valid pointers are passed to the renderTreeEdit functions.
|
// Nominal type to ensure only valid pointers are passed to the renderTreeEdit functions.
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
ArraySegment<RenderTreeNode> newTree)
|
ArraySegment<RenderTreeNode> newTree)
|
||||||
{
|
{
|
||||||
_entriesInUse = 0;
|
_entriesInUse = 0;
|
||||||
AppendDiffEntriesForRange(oldTree.Array, 0, oldTree.Count, newTree.Array, 0, newTree.Count);
|
var siblingIndex = 0;
|
||||||
TrimTrailingContinueNodes();
|
AppendDiffEntriesForRange(oldTree.Array, 0, oldTree.Count, newTree.Array, 0, newTree.Count, ref siblingIndex);
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -34,7 +34,8 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|
|
||||||
private void AppendDiffEntriesForRange(
|
private void AppendDiffEntriesForRange(
|
||||||
RenderTreeNode[] oldTree, int oldStartIndex, int oldEndIndexExcl,
|
RenderTreeNode[] oldTree, int oldStartIndex, int oldEndIndexExcl,
|
||||||
RenderTreeNode[] newTree, int newStartIndex, int newEndIndexExcl)
|
RenderTreeNode[] newTree, int newStartIndex, int newEndIndexExcl,
|
||||||
|
ref int siblingIndex)
|
||||||
{
|
{
|
||||||
var hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
var hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
||||||
var hasMoreNew = newEndIndexExcl > newStartIndex;
|
var hasMoreNew = newEndIndexExcl > newStartIndex;
|
||||||
|
|
@ -47,7 +48,7 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|
|
||||||
if (oldSeq == newSeq)
|
if (oldSeq == newSeq)
|
||||||
{
|
{
|
||||||
AppendDiffEntriesForNodesWithSameSequence(oldTree, oldStartIndex, newTree, newStartIndex);
|
AppendDiffEntriesForNodesWithSameSequence(oldTree, oldStartIndex, newTree, newStartIndex, ref siblingIndex);
|
||||||
oldStartIndex = NextSiblingIndex(oldTree, oldStartIndex);
|
oldStartIndex = NextSiblingIndex(oldTree, oldStartIndex);
|
||||||
newStartIndex = NextSiblingIndex(newTree, newStartIndex);
|
newStartIndex = NextSiblingIndex(newTree, newStartIndex);
|
||||||
hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
||||||
|
|
@ -124,13 +125,14 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
if (newTree[newStartIndex].NodeType == RenderTreeNodeType.Attribute)
|
if (newTree[newStartIndex].NodeType == RenderTreeNodeType.Attribute)
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.SetAttribute(newStartIndex));
|
Append(RenderTreeEdit.SetAttribute(siblingIndex, newStartIndex));
|
||||||
newStartIndex++;
|
newStartIndex++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.PrependNode(newStartIndex));
|
Append(RenderTreeEdit.PrependNode(siblingIndex, newStartIndex));
|
||||||
newStartIndex = NextSiblingIndex(newTree, newStartIndex);
|
newStartIndex = NextSiblingIndex(newTree, newStartIndex);
|
||||||
|
siblingIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMoreNew = newEndIndexExcl > newStartIndex;
|
hasMoreNew = newEndIndexExcl > newStartIndex;
|
||||||
|
|
@ -140,12 +142,12 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
if (oldTree[oldStartIndex].NodeType == RenderTreeNodeType.Attribute)
|
if (oldTree[oldStartIndex].NodeType == RenderTreeNodeType.Attribute)
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.RemoveAttribute(oldTree[oldStartIndex].AttributeName));
|
Append(RenderTreeEdit.RemoveAttribute(siblingIndex, oldTree[oldStartIndex].AttributeName));
|
||||||
oldStartIndex++;
|
oldStartIndex++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.RemoveNode());
|
Append(RenderTreeEdit.RemoveNode(siblingIndex));
|
||||||
oldStartIndex = NextSiblingIndex(oldTree, oldStartIndex);
|
oldStartIndex = NextSiblingIndex(oldTree, oldStartIndex);
|
||||||
}
|
}
|
||||||
hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
||||||
|
|
@ -163,7 +165,8 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|
|
||||||
private void AppendDiffEntriesForNodesWithSameSequence(
|
private void AppendDiffEntriesForNodesWithSameSequence(
|
||||||
RenderTreeNode[] oldTree, int oldNodeIndex,
|
RenderTreeNode[] oldTree, int oldNodeIndex,
|
||||||
RenderTreeNode[] newTree, int newNodeIndex)
|
RenderTreeNode[] newTree, int newNodeIndex,
|
||||||
|
ref int siblingIndex)
|
||||||
{
|
{
|
||||||
// We can assume that the old and new nodes are of the same type, because they correspond
|
// We can assume that the old and new nodes are of the same type, because they correspond
|
||||||
// to the same sequence number (and if not, the behaviour is undefined).
|
// to the same sequence number (and if not, the behaviour is undefined).
|
||||||
|
|
@ -175,11 +178,11 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
var newText = newTree[newNodeIndex].TextContent;
|
var newText = newTree[newNodeIndex].TextContent;
|
||||||
if (string.Equals(oldText, newText, StringComparison.Ordinal))
|
if (string.Equals(oldText, newText, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.Continue());
|
siblingIndex++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.UpdateText(newNodeIndex));
|
Append(RenderTreeEdit.UpdateText(siblingIndex, newNodeIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +199,8 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
// Diff the attributes
|
// Diff the attributes
|
||||||
AppendDiffEntriesForRange(
|
AppendDiffEntriesForRange(
|
||||||
oldTree, oldNodeIndex + 1, oldNodeAttributesEndIndexExcl,
|
oldTree, oldNodeIndex + 1, oldNodeAttributesEndIndexExcl,
|
||||||
newTree, newNodeIndex + 1, newNodeAttributesEndIndexExcl);
|
newTree, newNodeIndex + 1, newNodeAttributesEndIndexExcl,
|
||||||
|
ref siblingIndex);
|
||||||
|
|
||||||
// Diff the children
|
// Diff the children
|
||||||
var oldNodeChildrenEndIndexExcl = oldTree[oldNodeIndex].ElementDescendantsEndIndex + 1;
|
var oldNodeChildrenEndIndexExcl = oldTree[oldNodeIndex].ElementDescendantsEndIndex + 1;
|
||||||
|
|
@ -206,22 +210,26 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
newNodeChildrenEndIndexExcl > newNodeAttributesEndIndexExcl;
|
newNodeChildrenEndIndexExcl > newNodeAttributesEndIndexExcl;
|
||||||
if (hasChildrenToProcess)
|
if (hasChildrenToProcess)
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.StepIn());
|
Append(RenderTreeEdit.StepIn(siblingIndex));
|
||||||
|
var childSiblingIndex = 0;
|
||||||
AppendDiffEntriesForRange(
|
AppendDiffEntriesForRange(
|
||||||
oldTree, oldNodeAttributesEndIndexExcl, oldNodeChildrenEndIndexExcl,
|
oldTree, oldNodeAttributesEndIndexExcl, oldNodeChildrenEndIndexExcl,
|
||||||
newTree, newNodeAttributesEndIndexExcl, newNodeChildrenEndIndexExcl);
|
newTree, newNodeAttributesEndIndexExcl, newNodeChildrenEndIndexExcl,
|
||||||
|
ref childSiblingIndex);
|
||||||
Append(RenderTreeEdit.StepOut());
|
Append(RenderTreeEdit.StepOut());
|
||||||
|
siblingIndex++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.Continue());
|
siblingIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Elements with different names are treated as completely unrelated
|
// Elements with different names are treated as completely unrelated
|
||||||
Append(RenderTreeEdit.PrependNode(newNodeIndex));
|
Append(RenderTreeEdit.PrependNode(siblingIndex, newNodeIndex));
|
||||||
Append(RenderTreeEdit.RemoveNode());
|
siblingIndex++;
|
||||||
|
Append(RenderTreeEdit.RemoveNode(siblingIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -236,14 +244,14 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
// instance of any changes
|
// instance of any changes
|
||||||
// TODO: Also copy the existing child component instance to the new
|
// TODO: Also copy the existing child component instance to the new
|
||||||
// tree so we can preserve it across multiple updates
|
// tree so we can preserve it across multiple updates
|
||||||
|
siblingIndex++;
|
||||||
Append(RenderTreeEdit.Continue());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Child components of different types are treated as completely unrelated
|
// Child components of different types are treated as completely unrelated
|
||||||
Append(RenderTreeEdit.PrependNode(newNodeIndex));
|
Append(RenderTreeEdit.PrependNode(siblingIndex, newNodeIndex));
|
||||||
Append(RenderTreeEdit.RemoveNode());
|
siblingIndex++;
|
||||||
|
Append(RenderTreeEdit.RemoveNode(siblingIndex));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +267,7 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|| oldTree[oldNodeIndex].AttributeEventHandlerValue != newTree[newNodeIndex].AttributeEventHandlerValue;
|
|| oldTree[oldNodeIndex].AttributeEventHandlerValue != newTree[newNodeIndex].AttributeEventHandlerValue;
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
Append(RenderTreeEdit.SetAttribute(newNodeIndex));
|
Append(RenderTreeEdit.SetAttribute(siblingIndex, newNodeIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -267,8 +275,8 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
// Since this code path is never reachable for Razor components (because you
|
// Since this code path is never reachable for Razor components (because you
|
||||||
// can't have two different attribute names from the same source sequence), we
|
// can't have two different attribute names from the same source sequence), we
|
||||||
// could consider removing the 'name equality' check entirely for perf
|
// could consider removing the 'name equality' check entirely for perf
|
||||||
Append(RenderTreeEdit.SetAttribute(newNodeIndex));
|
Append(RenderTreeEdit.SetAttribute(siblingIndex, newNodeIndex));
|
||||||
Append(RenderTreeEdit.RemoveAttribute(oldName));
|
Append(RenderTreeEdit.RemoveAttribute(siblingIndex, oldName));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -297,14 +305,11 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
if (entry.Type == RenderTreeEditType.StepOut)
|
if (entry.Type == RenderTreeEditType.StepOut)
|
||||||
{
|
{
|
||||||
TrimTrailingContinueNodes();
|
// If the preceding node is a StepIn, then the StepOut cancels it out
|
||||||
|
|
||||||
// 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 == RenderTreeEditType.StepIn)
|
if (_entriesInUse > 0 && _entries[_entriesInUse - 1].Type == RenderTreeEditType.StepIn)
|
||||||
{
|
{
|
||||||
_entriesInUse--;
|
_entriesInUse--;
|
||||||
entry = RenderTreeEdit.Continue();
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,13 +320,5 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
|
|
||||||
_entries[_entriesInUse++] = entry;
|
_entries[_entriesInUse++] = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TrimTrailingContinueNodes()
|
|
||||||
{
|
|
||||||
while (_entriesInUse > 0 && _entries[_entriesInUse - 1].Type == RenderTreeEditType.Continue)
|
|
||||||
{
|
|
||||||
_entriesInUse--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RenderTreeEditType Type { get; private set; }
|
public RenderTreeEditType Type { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index of the sibling node that the edit relates to.
|
||||||
|
/// </summary>
|
||||||
|
public int SiblingIndex { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the index of related data in an associated render tree. For example, if the
|
/// Gets the index of related data in an associated render tree. For example, if the
|
||||||
/// <see cref="Type"/> value is <see cref="RenderTreeEditType.PrependNode"/>, gets the
|
/// <see cref="Type"/> value is <see cref="RenderTreeEditType.PrependNode"/>, gets the
|
||||||
|
|
@ -26,43 +31,44 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RemovedAttributeName { get; private set; }
|
public string RemovedAttributeName { get; private set; }
|
||||||
|
|
||||||
internal static RenderTreeEdit Continue() => new RenderTreeEdit
|
internal static RenderTreeEdit RemoveNode(int siblingIndex) => new RenderTreeEdit
|
||||||
{
|
{
|
||||||
Type = RenderTreeEditType.Continue
|
Type = RenderTreeEditType.RemoveNode,
|
||||||
|
SiblingIndex = siblingIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeEdit RemoveNode() => new RenderTreeEdit
|
internal static RenderTreeEdit PrependNode(int siblingIndex, int newTreeIndex) => new RenderTreeEdit
|
||||||
{
|
|
||||||
Type = RenderTreeEditType.RemoveNode
|
|
||||||
};
|
|
||||||
|
|
||||||
internal static RenderTreeEdit PrependNode(int newTreeIndex) => new RenderTreeEdit
|
|
||||||
{
|
{
|
||||||
Type = RenderTreeEditType.PrependNode,
|
Type = RenderTreeEditType.PrependNode,
|
||||||
|
SiblingIndex = siblingIndex,
|
||||||
NewTreeIndex = newTreeIndex
|
NewTreeIndex = newTreeIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeEdit UpdateText(int newTreeIndex) => new RenderTreeEdit
|
internal static RenderTreeEdit UpdateText(int siblingIndex, int newTreeIndex) => new RenderTreeEdit
|
||||||
{
|
{
|
||||||
Type = RenderTreeEditType.UpdateText,
|
Type = RenderTreeEditType.UpdateText,
|
||||||
|
SiblingIndex = siblingIndex,
|
||||||
NewTreeIndex = newTreeIndex
|
NewTreeIndex = newTreeIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeEdit SetAttribute(int newNodeIndex) => new RenderTreeEdit
|
internal static RenderTreeEdit SetAttribute(int siblingIndex, int newNodeIndex) => new RenderTreeEdit
|
||||||
{
|
{
|
||||||
Type = RenderTreeEditType.SetAttribute,
|
Type = RenderTreeEditType.SetAttribute,
|
||||||
|
SiblingIndex = siblingIndex,
|
||||||
NewTreeIndex = newNodeIndex
|
NewTreeIndex = newNodeIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeEdit RemoveAttribute(string name) => new RenderTreeEdit
|
internal static RenderTreeEdit RemoveAttribute(int siblingIndex, string name) => new RenderTreeEdit
|
||||||
{
|
{
|
||||||
Type = RenderTreeEditType.RemoveAttribute,
|
Type = RenderTreeEditType.RemoveAttribute,
|
||||||
|
SiblingIndex = siblingIndex,
|
||||||
RemovedAttributeName = name
|
RemovedAttributeName = name
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeEdit StepIn() => new RenderTreeEdit
|
internal static RenderTreeEdit StepIn(int siblingIndex) => new RenderTreeEdit
|
||||||
{
|
{
|
||||||
Type = RenderTreeEditType.StepIn
|
Type = RenderTreeEditType.StepIn,
|
||||||
|
SiblingIndex = siblingIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeEdit StepOut() => new RenderTreeEdit
|
internal static RenderTreeEdit StepOut() => new RenderTreeEdit
|
||||||
|
|
|
||||||
|
|
@ -9,52 +9,41 @@ namespace Microsoft.Blazor.RenderTree
|
||||||
public enum RenderTreeEditType: int
|
public enum RenderTreeEditType: int
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that there are no further operations on the current tree node, and
|
/// Indicates that a new node should be inserted before the specified tree node.
|
||||||
/// so the edit position should move to the next sibling (or if there is no next
|
|
||||||
/// sibling, then to the position where one would be).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Continue = 1,
|
PrependNode = 1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that a new node should be inserted before the current tree node.
|
/// Indicates that the specified tree node should be removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PrependNode = 2,
|
RemoveNode = 2,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the current tree node should be removed, and that the edit position
|
/// Indicates that an attribute value should be applied to the specified node.
|
||||||
/// should then move to the next sibling (or if there is no next sibling, then to the
|
|
||||||
/// position where one would be).
|
|
||||||
/// </summary>
|
|
||||||
RemoveNode = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates that an attribute value should be applied to the current node.
|
|
||||||
/// This may be a change to an existing attribute, or the addition of a new attribute.
|
/// This may be a change to an existing attribute, or the addition of a new attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SetAttribute = 4,
|
SetAttribute = 3,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that a named attribute should be removed from the current node.
|
/// Indicates that a named attribute should be removed from the specified node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
RemoveAttribute = 5,
|
RemoveAttribute = 4,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the text content of the current node (which must be a text node)
|
/// Indicates that the text content of the specified node (which must be a text node)
|
||||||
/// should be updated.
|
/// should be updated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
UpdateText = 6,
|
UpdateText = 5,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that the edit position should move inside the current node to its first
|
/// Indicates that the edit position should move inside the specified node.
|
||||||
/// child (or, if there are no children, to a point where a first child would be).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
StepIn = 7,
|
StepIn = 6,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that there are no further edit operations on the current node, and the
|
/// Indicates that there are no further edit operations on the current node, and the
|
||||||
/// edit position should move to the next sibling of the parent node (or if it does not
|
/// edit position should move back to the parent node.
|
||||||
/// have a next sibling, then to the position where one would be).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
StepOut = 8,
|
StepOut = 7,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,10 +63,9 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 1);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -89,8 +88,7 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1));
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -112,9 +110,8 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1),
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1));
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -136,15 +133,14 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 1);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 2);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -168,10 +164,8 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 2),
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 2));
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
|
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -193,16 +187,14 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 2);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 3);
|
||||||
Assert.Equal(3, entry.NewTreeIndex);
|
Assert.Equal(3, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -226,15 +218,14 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 1);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 2);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -258,9 +249,8 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1),
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1));
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -278,8 +268,8 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 0),
|
||||||
entry => Assert.Equal(RenderTreeEditType.PrependNode, entry.Type));
|
entry => AssertEdit(entry, RenderTreeEditType.PrependNode, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -299,7 +289,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.UpdateText, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.UpdateText, 0);
|
||||||
Assert.Equal(0, entry.NewTreeIndex);
|
Assert.Equal(0, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -327,10 +317,10 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 0);
|
||||||
Assert.Equal(0, entry.NewTreeIndex);
|
Assert.Equal(0, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -350,10 +340,10 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.PrependNode, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.PrependNode, 0);
|
||||||
Assert.Equal(0, entry.NewTreeIndex);
|
Assert.Equal(0, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry => Assert.Equal(RenderTreeEditType.RemoveNode, entry.Type));
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveNode, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -378,7 +368,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.SetAttribute, 0);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -405,7 +395,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.RemoveAttribute, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.RemoveAttribute, 0);
|
||||||
Assert.Equal("will be removed", entry.RemovedAttributeName);
|
Assert.Equal("will be removed", entry.RemovedAttributeName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -433,7 +423,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.SetAttribute, 0);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -464,7 +454,7 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.SetAttribute, 0);
|
||||||
Assert.Equal(2, entry.NewTreeIndex);
|
Assert.Equal(2, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -490,12 +480,12 @@ namespace Microsoft.Blazor.Test
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.SetAttribute, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.SetAttribute, 0);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.RemoveAttribute, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.RemoveAttribute, 0);
|
||||||
Assert.Equal("oldname", entry.RemovedAttributeName);
|
Assert.Equal("oldname", entry.RemovedAttributeName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -507,6 +497,7 @@ namespace Microsoft.Blazor.Test
|
||||||
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
var oldTree = new RenderTreeBuilder(new FakeRenderer());
|
||||||
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
var newTree = new RenderTreeBuilder(new FakeRenderer());
|
||||||
var diff = new RenderTreeDiffComputer();
|
var diff = new RenderTreeDiffComputer();
|
||||||
|
oldTree.AddText(09, "unrelated");
|
||||||
oldTree.OpenElement(10, "root");
|
oldTree.OpenElement(10, "root");
|
||||||
oldTree.OpenElement(11, "child");
|
oldTree.OpenElement(11, "child");
|
||||||
oldTree.OpenElement(12, "grandchild");
|
oldTree.OpenElement(12, "grandchild");
|
||||||
|
|
@ -515,6 +506,7 @@ namespace Microsoft.Blazor.Test
|
||||||
oldTree.CloseElement();
|
oldTree.CloseElement();
|
||||||
oldTree.CloseElement();
|
oldTree.CloseElement();
|
||||||
|
|
||||||
|
newTree.AddText(09, "unrelated");
|
||||||
newTree.OpenElement(10, "root");
|
newTree.OpenElement(10, "root");
|
||||||
newTree.OpenElement(11, "child");
|
newTree.OpenElement(11, "child");
|
||||||
newTree.OpenElement(12, "grandchild");
|
newTree.OpenElement(12, "grandchild");
|
||||||
|
|
@ -528,17 +520,17 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.StepIn, 1),
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.StepIn, 0),
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.StepIn, 0),
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.UpdateText, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.UpdateText, 0);
|
||||||
Assert.Equal(3, entry.NewTreeIndex);
|
Assert.Equal(4, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepOut, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0),
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepOut, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0),
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepOut, entry.Type));
|
entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -571,13 +563,13 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepIn, entry.Type),
|
entry => AssertEdit(entry, RenderTreeEditType.StepIn, 0),
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.UpdateText, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.UpdateText, 0);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
},
|
},
|
||||||
entry => Assert.Equal(RenderTreeEditType.StepOut, entry.Type));
|
entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -601,10 +593,9 @@ namespace Microsoft.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(result.Edits,
|
Assert.Collection(result.Edits,
|
||||||
entry => Assert.Equal(RenderTreeEditType.Continue, entry.Type),
|
|
||||||
entry =>
|
entry =>
|
||||||
{
|
{
|
||||||
Assert.Equal(RenderTreeEditType.UpdateText, entry.Type);
|
AssertEdit(entry, RenderTreeEditType.UpdateText, 1);
|
||||||
Assert.Equal(1, entry.NewTreeIndex);
|
Assert.Equal(1, entry.NewTreeIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -626,5 +617,14 @@ namespace Microsoft.Blazor.Test
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AssertEdit(
|
||||||
|
RenderTreeEdit edit,
|
||||||
|
RenderTreeEditType type,
|
||||||
|
int siblingIndex)
|
||||||
|
{
|
||||||
|
Assert.Equal(type, edit.Type);
|
||||||
|
Assert.Equal(siblingIndex, edit.SiblingIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue