This PR renames `language::Buffer::new` to `language::Buffer::local` and
simplifies its interface. Instead of taking a replica id (which should
always be 0 for the local case) and a `BufferId`, which was awkward and
verbose to construct, it simply takes text and a `cx`.
It uses the `cx` to derive a `BufferId` from the `EntityId` associated
with the `cx`, which should always be positive based on the following
analysis...
We convert the entity id to a u64 using this method on `EntityId`, which
is defined by macros in the `slotmap` crate:
```rust
pub fn as_ffi(self) -> u64 {
(u64::from(self.version.get()) << 32) | u64::from(self.idx)
}
```
If you look at the type of `version` in `KeyData`, it is non-zero:
```rust
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyData {
idx: u32,
version: NonZeroU32,
}
```
This commit also adds `Context::reserve_model` and
`Context::insert_model` to determine a model's entity ID before it is
created, which we need in order to assign a `BufferId` in the background
when loading a buffer asynchronously.
Release Notes:
- N/A
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
294 lines
8.7 KiB
Rust
294 lines
8.7 KiB
Rust
use crate::{
|
|
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
|
|
EventEmitter, Model, Reservation, Subscription, Task, View, WeakModel, WindowContext,
|
|
WindowHandle,
|
|
};
|
|
use anyhow::Result;
|
|
use derive_more::{Deref, DerefMut};
|
|
use futures::FutureExt;
|
|
use std::{
|
|
any::{Any, TypeId},
|
|
borrow::{Borrow, BorrowMut},
|
|
future::Future,
|
|
};
|
|
|
|
/// The app context, with specialized behavior for the given model.
|
|
#[derive(Deref, DerefMut)]
|
|
pub struct ModelContext<'a, T> {
|
|
#[deref]
|
|
#[deref_mut]
|
|
app: &'a mut AppContext,
|
|
model_state: WeakModel<T>,
|
|
}
|
|
|
|
impl<'a, T: 'static> ModelContext<'a, T> {
|
|
pub(crate) fn new(app: &'a mut AppContext, model_state: WeakModel<T>) -> Self {
|
|
Self { app, model_state }
|
|
}
|
|
|
|
/// The entity id of the model backing this context.
|
|
pub fn entity_id(&self) -> EntityId {
|
|
self.model_state.entity_id
|
|
}
|
|
|
|
/// Returns a handle to the model belonging to this context.
|
|
pub fn handle(&self) -> Model<T> {
|
|
self.weak_model()
|
|
.upgrade()
|
|
.expect("The entity must be alive if we have a model context")
|
|
}
|
|
|
|
/// Returns a weak handle to the model belonging to this context.
|
|
pub fn weak_model(&self) -> WeakModel<T> {
|
|
self.model_state.clone()
|
|
}
|
|
|
|
/// Arranges for the given function to be called whenever [`ModelContext::notify`] or
|
|
/// [`ViewContext::notify`](crate::ViewContext::notify) is called with the given model or view.
|
|
pub fn observe<W, E>(
|
|
&mut self,
|
|
entity: &E,
|
|
mut on_notify: impl FnMut(&mut T, E, &mut ModelContext<'_, T>) + 'static,
|
|
) -> Subscription
|
|
where
|
|
T: 'static,
|
|
W: 'static,
|
|
E: Entity<W>,
|
|
{
|
|
let this = self.weak_model();
|
|
self.app.observe_internal(entity, move |e, cx| {
|
|
if let Some(this) = this.upgrade() {
|
|
this.update(cx, |this, cx| on_notify(this, e, cx));
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Subscribe to an event type from another model or view
|
|
pub fn subscribe<T2, E, Evt>(
|
|
&mut self,
|
|
entity: &E,
|
|
mut on_event: impl FnMut(&mut T, E, &Evt, &mut ModelContext<'_, T>) + 'static,
|
|
) -> Subscription
|
|
where
|
|
T: 'static,
|
|
T2: 'static + EventEmitter<Evt>,
|
|
E: Entity<T2>,
|
|
Evt: 'static,
|
|
{
|
|
let this = self.weak_model();
|
|
self.app.subscribe_internal(entity, move |e, event, cx| {
|
|
if let Some(this) = this.upgrade() {
|
|
this.update(cx, |this, cx| on_event(this, e, event, cx));
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
}
|
|
|
|
/// Register a callback to be invoked when GPUI releases this model.
|
|
pub fn on_release(
|
|
&mut self,
|
|
on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
|
|
) -> Subscription
|
|
where
|
|
T: 'static,
|
|
{
|
|
let (subscription, activate) = self.app.release_listeners.insert(
|
|
self.model_state.entity_id,
|
|
Box::new(move |this, cx| {
|
|
let this = this.downcast_mut().expect("invalid entity type");
|
|
on_release(this, cx);
|
|
}),
|
|
);
|
|
activate();
|
|
subscription
|
|
}
|
|
|
|
/// Register a callback to be run on the release of another model or view
|
|
pub fn observe_release<T2, E>(
|
|
&mut self,
|
|
entity: &E,
|
|
on_release: impl FnOnce(&mut T, &mut T2, &mut ModelContext<'_, T>) + 'static,
|
|
) -> Subscription
|
|
where
|
|
T: Any,
|
|
T2: 'static,
|
|
E: Entity<T2>,
|
|
{
|
|
let entity_id = entity.entity_id();
|
|
let this = self.weak_model();
|
|
let (subscription, activate) = self.app.release_listeners.insert(
|
|
entity_id,
|
|
Box::new(move |entity, cx| {
|
|
let entity = entity.downcast_mut().expect("invalid entity type");
|
|
if let Some(this) = this.upgrade() {
|
|
this.update(cx, |this, cx| on_release(this, entity, cx));
|
|
}
|
|
}),
|
|
);
|
|
activate();
|
|
subscription
|
|
}
|
|
|
|
/// Register a callback to for updates to the given global
|
|
pub fn observe_global<G: 'static>(
|
|
&mut self,
|
|
mut f: impl FnMut(&mut T, &mut ModelContext<'_, T>) + 'static,
|
|
) -> Subscription
|
|
where
|
|
T: 'static,
|
|
{
|
|
let handle = self.weak_model();
|
|
let (subscription, activate) = self.global_observers.insert(
|
|
TypeId::of::<G>(),
|
|
Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()),
|
|
);
|
|
self.defer(move |_| activate());
|
|
subscription
|
|
}
|
|
|
|
/// Arrange for the given function to be invoked whenever the application is quit.
|
|
/// The future returned from this callback will be polled for up to [crate::SHUTDOWN_TIMEOUT] until the app fully quits.
|
|
pub fn on_app_quit<Fut>(
|
|
&mut self,
|
|
mut on_quit: impl FnMut(&mut T, &mut ModelContext<T>) -> Fut + 'static,
|
|
) -> Subscription
|
|
where
|
|
Fut: 'static + Future<Output = ()>,
|
|
T: 'static,
|
|
{
|
|
let handle = self.weak_model();
|
|
let (subscription, activate) = self.app.quit_observers.insert(
|
|
(),
|
|
Box::new(move |cx| {
|
|
let future = handle.update(cx, |entity, cx| on_quit(entity, cx)).ok();
|
|
async move {
|
|
if let Some(future) = future {
|
|
future.await;
|
|
}
|
|
}
|
|
.boxed_local()
|
|
}),
|
|
);
|
|
activate();
|
|
subscription
|
|
}
|
|
|
|
/// Tell GPUI that this model has changed and observers of it should be notified.
|
|
pub fn notify(&mut self) {
|
|
if self
|
|
.app
|
|
.pending_notifications
|
|
.insert(self.model_state.entity_id)
|
|
{
|
|
self.app.pending_effects.push_back(Effect::Notify {
|
|
emitter: self.model_state.entity_id,
|
|
});
|
|
}
|
|
}
|
|
|
|
/// Spawn the future returned by the given function.
|
|
/// The function is provided a weak handle to the model owned by this context and a context that can be held across await points.
|
|
/// The returned task must be held or detached.
|
|
pub fn spawn<Fut, R>(&self, f: impl FnOnce(WeakModel<T>, AsyncAppContext) -> Fut) -> Task<R>
|
|
where
|
|
T: 'static,
|
|
Fut: Future<Output = R> + 'static,
|
|
R: 'static,
|
|
{
|
|
let this = self.weak_model();
|
|
self.app.spawn(|cx| f(this, cx))
|
|
}
|
|
}
|
|
|
|
impl<'a, T> ModelContext<'a, T> {
|
|
/// Emit an event of the specified type, which can be handled by other entities that have subscribed via `subscribe` methods on their respective contexts.
|
|
pub fn emit<Evt>(&mut self, event: Evt)
|
|
where
|
|
T: EventEmitter<Evt>,
|
|
Evt: 'static,
|
|
{
|
|
self.app.pending_effects.push_back(Effect::Emit {
|
|
emitter: self.model_state.entity_id,
|
|
event_type: TypeId::of::<Evt>(),
|
|
event: Box::new(event),
|
|
});
|
|
}
|
|
}
|
|
|
|
impl<'a, T> Context for ModelContext<'a, T> {
|
|
type Result<U> = U;
|
|
|
|
fn new_model<U: 'static>(
|
|
&mut self,
|
|
build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U,
|
|
) -> Model<U> {
|
|
self.app.new_model(build_model)
|
|
}
|
|
|
|
fn reserve_model<U: 'static>(&mut self) -> Reservation<U> {
|
|
self.app.reserve_model()
|
|
}
|
|
|
|
fn insert_model<U: 'static>(
|
|
&mut self,
|
|
reservation: Reservation<U>,
|
|
build_model: impl FnOnce(&mut ModelContext<'_, U>) -> U,
|
|
) -> Self::Result<Model<U>> {
|
|
self.app.insert_model(reservation, build_model)
|
|
}
|
|
|
|
fn update_model<U: 'static, R>(
|
|
&mut self,
|
|
handle: &Model<U>,
|
|
update: impl FnOnce(&mut U, &mut ModelContext<'_, U>) -> R,
|
|
) -> R {
|
|
self.app.update_model(handle, update)
|
|
}
|
|
|
|
fn read_model<U, R>(
|
|
&self,
|
|
handle: &Model<U>,
|
|
read: impl FnOnce(&U, &AppContext) -> R,
|
|
) -> Self::Result<R>
|
|
where
|
|
U: 'static,
|
|
{
|
|
self.app.read_model(handle, read)
|
|
}
|
|
|
|
fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
|
|
where
|
|
F: FnOnce(AnyView, &mut WindowContext<'_>) -> R,
|
|
{
|
|
self.app.update_window(window, update)
|
|
}
|
|
|
|
fn read_window<U, R>(
|
|
&self,
|
|
window: &WindowHandle<U>,
|
|
read: impl FnOnce(View<U>, &AppContext) -> R,
|
|
) -> Result<R>
|
|
where
|
|
U: 'static,
|
|
{
|
|
self.app.read_window(window, read)
|
|
}
|
|
}
|
|
|
|
impl<T> Borrow<AppContext> for ModelContext<'_, T> {
|
|
fn borrow(&self) -> &AppContext {
|
|
self.app
|
|
}
|
|
}
|
|
|
|
impl<T> BorrowMut<AppContext> for ModelContext<'_, T> {
|
|
fn borrow_mut(&mut self) -> &mut AppContext {
|
|
self.app
|
|
}
|
|
}
|