materializePositionAt method
GeneratedTreeSitterNode
materializePositionAt(
- GeneratedTreeSitterNode node,
- GeneratedTreeSitterNodePosition position
Materializes node at the exact position already computed by a relative
subtree cursor.
Native reusable nodes carry relative padding and size, and their absolute position is the cursor's accumulated byte offset. Re-applying every edit to the node's original absolute coordinates is not equivalent after a sequence of replacements and inverse edits, so incremental parsing must preserve the cursor's position here.
Implementation
GeneratedTreeSitterNode materializePositionAt(
GeneratedTreeSitterNode node,
GeneratedTreeSitterNodePosition position,
) {
final cache = _materializedPositionCaches[this] ??=
Map<GeneratedTreeSitterNode, GeneratedTreeSitterNode>.identity();
final cached = cache[node];
if (cached != null &&
cached.startByte == position.startByte &&
cached.endByte == position.endByte &&
_samePoint(cached.startPoint, position.startPoint) &&
_samePoint(cached.endPoint, position.endPoint)) {
return cached;
}
final positionChanged =
position.startByte != node.startByte ||
position.endByte != node.endByte ||
!_samePoint(position.startPoint, node.startPoint) ||
!_samePoint(position.endPoint, node.endPoint);
if (!hasChangesFor(node) && !positionChanged) return node;
final children = <GeneratedTreeSitterNode>[];
var childEndByte = position.startByte;
var childEndPoint = position.startPoint;
for (var index = 0; index < node.children.length; index++) {
final child = node.children[index];
final childState = editedSubtreeStateOf(child);
// A parent's start excludes the first child's padding. Every later
// child begins after its own padding relative to the preceding end.
final childStartByte = index == 0
? position.startByte
: childEndByte + childState.paddingBytes;
final childStartPoint = index == 0
? position.startPoint
: _pointAdd(childEndPoint, childState.paddingExtent);
final childPosition = GeneratedTreeSitterNodePosition(
startByte: childStartByte,
endByte: childStartByte + childState.sizeBytes,
startPoint: childStartPoint,
endPoint: _pointAdd(childStartPoint, childState.sizeExtent),
);
children.add(materializePositionAt(child, childPosition));
childEndByte = childPosition.endByte;
childEndPoint = childPosition.endPoint;
}
var childrenChanged = false;
for (var index = 0; index < children.length; index++) {
if (!identical(children[index], node.children[index])) {
childrenChanged = true;
break;
}
}
final editState = editedSubtreeStateOf(node);
final metricsChanged =
editState.paddingBytes != node.paddingBytes ||
!_samePoint(editState.paddingExtent, node.paddingExtent) ||
editState.sizeBytes != node.sizeBytes ||
!_samePoint(editState.sizeExtent, node.sizeExtent) ||
editState.hasChanges != node.hasChanges;
if (!childrenChanged && !positionChanged && !metricsChanged) {
return node;
}
final materialized = node.withEditedMetricsAndChildren(
startByte: position.startByte,
endByte: position.endByte,
startPoint: position.startPoint,
endPoint: position.endPoint,
paddingBytes: editState.paddingBytes,
paddingExtent: editState.paddingExtent,
sizeBytes: editState.sizeBytes,
sizeExtent: editState.sizeExtent,
hasChanges: editState.hasChanges,
children: children,
);
cache[node] = materialized;
return materialized;
}