Skip to content

Fragment parenting overhaul - #162

Merged
berendkleinhaneveld merged 7 commits into
masterfrom
fragment-parenting-overhaul
Jul 7, 2026
Merged

Fragment parenting overhaul#162
berendkleinhaneveld merged 7 commits into
masterfrom
fragment-parenting-overhaul

Conversation

@berendkleinhaneveld

@berendkleinhaneveld berendkleinhaneveld commented Feb 3, 2026

Copy link
Copy Markdown
Collaborator
  • Refactors fragment parenting to a consistent two-tree model:
    • template_children for static template structure
    • render_children() for currently rendered children
  • Adds iter_all_children() for full tree traversal across template + runtime-generated nodes.
  • Updates ComponentFragment, SlotFragment, ListFragment, ControlFlowFragment, and DynamicFragment to use the same traversal/parenting semantics.
  • Updates compiler slot registration so slot content (including directive-wrapped content) is registered only after slot_name is set.
  • Updates hot reload traversal/remount logic to use unified fragment traversal APIs.

berendkleinhaneveld and others added 7 commits February 3, 2026 14:29
Separate template tree (static structure) from render tree (dynamic state):

- Rename `children` → `template_children` for static structure
- Add `render_children()` method to return currently mounted fragments
- Add `render_parent` property for anchor lookups in slot contexts
- Change `slot_contents` → `slot_content: dict[str, list[Fragment]]`

Fragment-specific changes:
- ControlFlowFragment: `_active_child` tracks current branch
- ListFragment: `_generated_fragments` separate from template
- ComponentFragment: dual mode (usage site vs render wrapper)
- SlotFragment: sets `_render_parent` on slot content during mount

Compiler changes:
- Slot content created without parent, then slot_name set, then registered
- Ensures slot_name is read correctly during register_child()

Hot reload changes:
- Traverse ListFragment._generated_fragments
- Handle ComponentFragment.rendered_fragment in element lookup

All 253 tests pass.
- Add iter_all_children() method to Fragment base class and all subclasses
- ListFragment yields template_children + _generated_fragments
- ComponentFragment yields template_children + rendered_fragment + slot_content
- DynamicFragment yields template_children + _active_fragment

Simplify hot_reload.py using iter_all_children():
- _find_affected_recursive: reduced from 50+ lines to 15 lines
- _collect_used_modules_recursive: reduced from 25+ lines to 10 lines
- _find_root_element: now uses render_children() instead of manual checks
- _collect_state_recursive/_restore_state_recursive: use iter_all_children()

Removed unused DynamicFragment import from hot_reload.py.
Use render_children() to find fragment's position among siblings instead of
manually checking each fragment type's child collection. Slot content still
needs special handling since it's rendered by SlotFragment, not directly by
the ComponentFragment that holds it.

Removed ListFragment import (no longer needed).
Resolves conflicts in fragment.py by porting the perf work from
master (PRs #186-#188) onto the two-tree fragment model:

- Keep both _render_parent (branch) and _component_parent_cache
  (master) on Fragment.
- anchor() keeps the unified render_children() traversal, which
  subsumes master's separate children/slot_contents lookups.
- ListFragment.mount() keeps the lazy loop-invariant anchor from
  master, applied to _generated_fragments.
- ComponentFragment.mount() uses master's simpler
  `self.component._element = self.first()`.
- _invalidate_component_parent_cache() traverses via
  iter_all_children() instead of the removed children/slot_contents
  attributes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@berendkleinhaneveld
berendkleinhaneveld marked this pull request as ready for review July 7, 2026 20:01
@berendkleinhaneveld
berendkleinhaneveld merged commit e83a4c5 into master Jul 7, 2026
9 of 10 checks passed
@berendkleinhaneveld
berendkleinhaneveld deleted the fragment-parenting-overhaul branch July 7, 2026 20:01
berendkleinhaneveld added a commit that referenced this pull request Jul 21, 2026
Features:
- Add pure-Python view API as alternative to cgx templates (#193)
- Support text elements for PySide widgets that display text (#191)

Fixes & internals:
- Fragment parenting overhaul (#162)
- Fix PyInstaller hook for CGX files inside packages (#184)
- Write compiled AST to temp file when CGX_DEBUG is set (#175)

Performance:
- Speed up mount path: cheap arity check, reuse first(), leaner emit (#186)
- Cache Fragment._component_parent() lookups (#187)
- Avoid redundant anchor lookups in Fragment.anchor() and unkeyed v-for (#188)

Documentation:
- Add MkDocs documentation with GitHub Pages deployment (#176)
- Add internals architecture documentation page (#194)
- Add docs badge and links to README (#192)

Tooling & CI:
- Add benchmark suite and per-PR benchmark CI workflow (#185)
- Make benchmark CI guard robust against run-to-run noise (#196)
- Update GitHub actions from Node 20 to Node 24 (#190)
- Migrate from pre-commit to prek (#195)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant