gpui: Add ReadEntity and Projection entity handles - #60740
Closed
mikayla-maki wants to merge 8 commits into
Closed
Conversation
mikayla-maki
marked this pull request as draft
July 11, 2026 23:01
Completes the entity handle lattice with read-only and lensed capability types: - ReadEntity<T> / WeakReadEntity<T>: strong and weak read-only handles, restoring the read/write distinction that shared-mutability entities erase. - Projection<P> / ProjectionMut<P> and weak variants: source-erased lens handles over part of an entity's state, so components can accept any P-shaped state without dictating how callers store it. Lenses are non-capturing function pointers, making projections allocation-free. - AnyEntity::downcast_ref (backed by repr(transparent) on Entity) and EntityMap::read_any, so projection reads and writes avoid handle clones.
Lens projections now get their identity from a per-call-site relay
entity, memoized like use_state, so sibling projections of different
fields of one entity no longer collide when used as view identities.
- Entity::project/project_mut are crate-private; public construction is
Window::use_projection{,_mut} and use_keyed_projection{,_mut} (or the
identity From<Entity<P>> conversions).
- The relay entity forwards source notifications, so observation and
caching keyed on the projection work.
- The project! macro derives both lenses of a mutable projection from a
single field path.
…lenses Removes all unsafe from projections: the type-erased lens fn pointers, their transmuting trampolines, AnyEntity::downcast_ref, and the repr(transparent) on Entity<T>. Projection handles now point at a state entity that owns the source handle, lenses, and subscription, so escaped handles retarget when a hook's source entity is swapped, and observers are notified of the swap.
…t!(mut) Replaces the private test constructors and the three per-test HookView definitions with a single harness that runs a hook during render, so tests build projections through the public use_projection path. project! is now read-only by default, with a mut prefix for the writable form.
mikayla-maki
force-pushed
the
fable/gpui-smart-pointer-types
branch
from
July 30, 2026 21:00
3a2ecee to
480c680
Compare
The example now keeps its form in a single Profile entity and hands each component a projection of the field it edits, built inline in the element tree with project!. Input, TextArea and Editor take ProjectionMut<String> instead of Entity<String>, so a component works the same whether its text is a whole entity or one field of a larger struct. Adds Projection::observe, which Editor needs to clamp its cursor when the value is written from outside.
mikayla-maki
force-pushed
the
fable/gpui-smart-pointer-types
branch
from
July 30, 2026 21:42
1d7f04d to
6a93d1c
Compare
A projection's source can now be another projection, so a component handed a ProjectionMut<Person> can project its fields without knowing whether the person is an entity or a field of something larger. Composition walks lenses rather than copying: writes recurse down to the entity that owns the value, notifications climb back up. Also fixes a pre-existing feedback loop in the example. A view's identity is the notify target for state allocated inside it, so an Input identified by the value it allocates an editor over routed that editor's notifications back into the value it observes and spun forever. This reproduced on the example before any projection work, with a plain Entity<String>.
Contributor
|
To keep the PR queue cleaner, will close this PR for now, as this is a draft still with no changes for over 3 weeks. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the entity handle lattice with read-only and lensed capability types:
Entity<T>ProjectionMut<P>ReadEntity<T>Projection<P>each with a weak twin (
WeakReadEntity,WeakProjection,WeakProjectionMut), and conversions flowing write→read and whole→lens.ReadEntity<T>restores the read/write distinction that shared-mutability entities erase: holders can read (and be re-rendered via access tracking) but cannot update or notify, making an entity's writer set auditable.Projection<P>/ProjectionMut<P>are source-erased lens handles over part of an entity's state, so components can accept anyP-shaped state (impl Into<ProjectionMut<String>>) without dictating how callers store it.Pmay be unsized (Projection<str>). Writes route through the source entity and always notify it.Window::use_projection/use_projection_mut(anduse_keyed_*variants for loops), or by converting anEntity<P>withFrom. Each hook call site gets a memoized relay entity (likeuse_state) as the projection's identity, so sibling projections of different fields of one entity don't collide when used as view identities; the relay forwards source notifications so observation and caching keyed on the projection work. There is deliberately no way to construct a lens projection outside a render context — state that needs identity independent of any view should be a proper entity.project!macro derives both lenses of a mutable projection from a single field path:project!(window, cx, &person, address.city).AnyEntity::downcast_ref(backed by#[repr(transparent)]onEntity<T>) andEntityMap::read_any, so projection reads and writes avoid handle clones and refcount traffic.Deliberate omissions, discussed and deferred: no equality-based notify dedupe (projecting from a notify-happy entity is a state-modeling smell — split the hot field into its own entity), and no
observe/subscribeon read handles yet.Release Notes: