simvx.editor.scene_diff

Reconcile a parsed scene class with a live runtime Node tree.

The editor mutates the runtime tree directly while the user works (drag a node, change a Property, add a child); on save we need to push those mutations back into the on-disk source without losing comments, blank lines, hand-written helper functions, or import ordering.

func:

apply_runtime_diff is the integration point: given a

class:

~simvx.core.scene_io.SceneClass (a parso-backed view of the class in the user’s .py file) and the live runtime root, it issues the minimum set of structural edits via the SceneClass API so the next

meth:

SceneFile.save writes a file whose __init__ matches the runtime tree exactly.

Identity matching uses the same name-sanitisation rule the greenfield emitter applies (:func:structural_type_name + _canonical_var_names below): each runtime child gets its var name computed; if the parsed source already has a child with that name, the two are treated as the same node and reconciled in place. Otherwise it’s an add (for runtime- only nodes) or a remove (for source-only nodes).

v1 limitations (locked down by tests):

  • Renaming a runtime child mid-session manifests as a remove + add at save time. Proper rename detection requires a stable identity beyond the user-visible .name, which is a v2 concern.

  • Procedural __init__ bodies (children built inside loops or conditionals) are not safe to diff-reconcile; callers should detect that case via :func:~simvx.core.scene_io.has_procedural_construction and fall back to greenfield save.

Module Contents

Functions

apply_runtime_diff

Reconcile scene_class so its __init__ matches runtime_root.

Data

API

simvx.editor.scene_diff.__all__

[‘apply_runtime_diff’]

simvx.editor.scene_diff.apply_runtime_diff(scene_class: simvx.core.scene_io.SceneClass, runtime_root: simvx.core.Node, *, identity_hints: dict[simvx.core.Node, str] | None = None) None[source]

Reconcile scene_class so its __init__ matches runtime_root.

Mutation strategy:

  1. Update root super().__init__ kwargs to reflect non-default Property values on the runtime root (insert/update/remove).

  2. For each top-level runtime child whose canonical var name already exists in the source, update its constructor kwargs in place.

  3. Append runtime children whose canonical name has no source counterpart via :meth:SceneClass.add_child (auto-importing the type via the file’s :class:ImportSet).

  4. Remove source-only children via :meth:SceneClass.remove_child, then drop now-unused imports for types no longer referenced by any remaining child constructor.

  5. If the surviving children are out of order relative to the runtime tree, call :meth:SceneClass.reorder_children.

Only top-level children of the root are reconciled in v1 (matching the emitter’s depth: nested add_child calls aren’t expressed in the current scene-class shape). Deeper sub-tree reconciliation is a v2 concern.

identity_hints is an optional {runtime_node: source_var_name} mapping captured at scene load. When a runtime node has a hint and its current canonical var name differs from the hint, the diff treats this as a rename — the source var is renamed in place via

Meth:

SceneClass.rename_child and kwargs/type-swap are reconciled against the renamed source — rather than emitting a remove + add seam (which would lose the original source position and any kwargs the source had that the runtime no longer carries explicitly).