replaceById function

CanvasSceneDocument replaceById(
  1. CanvasSceneDocument doc,
  2. ElementId id,
  3. Node updated
)

Replace node id with updated anywhere in the tree.

If id does not exist, returns doc unchanged.

A replacement must preserve the target node ID. When the replacement changes the target's descendants, its subtree IDs must also remain nonblank, internally unique, and collision-free with nodes outside the subtree being replaced.

Implementation

CanvasSceneDocument replaceById(
  CanvasSceneDocument doc,
  ElementId id,
  Node updated,
) {
  final current = findById(doc, id);

  // Preserve the existing missing-target no-op contract.
  if (current == null) return doc;

  if (updated.id != id) {
    throw ArgumentError.value(
      updated.id,
      'updated.id',
      'Replacement node ID must match target ID "$id".',
    );
  }

  if (_replacementChangesDescendants(current, updated)) {
    // IDs belonging to the old subtree are intentionally excluded from the
    // collision set. The replacement is allowed to retain or rearrange those
    // IDs because the old subtree disappears atomically when replacement
    // succeeds.
    final replacedIds = collectAllNodeIds(root: current);

    final outsideIds = collectAllNodeIds(doc: doc)..removeAll(replacedIds);

    _validateReplacementSubtreeIds(updated, outsideIds);
  }

  return rewriteSceneDocument(
    doc,
    (n) => rewritePostOrder(
      n,
      (m) => (m.id == id) ? updated : m,
      prune: (m) => m.id == id,
    ),
  );
}