Improve the ergonomics of creating local buffers (#10347)

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>
This commit is contained in:
Nathan Sobo
2024-04-10 08:32:51 -06:00
committed by GitHub
co-authored by Piotr Osiewicz
parent 664efef76b
commit 7abb63cfda
31 changed files with 376 additions and 519 deletions
+44 -9
View File
@@ -1,7 +1,7 @@
use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context,
DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, Result,
Task, View, ViewContext, VisualContext, WindowContext, WindowHandle,
DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render,
Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle,
};
use anyhow::{anyhow, Context as _};
use derive_more::{Deref, DerefMut};
@@ -35,6 +35,28 @@ impl Context for AsyncAppContext {
Ok(app.new_model(build_model))
}
fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
let app = self
.app
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut app = app.borrow_mut();
Ok(app.reserve_model())
}
fn insert_model<T: 'static>(
&mut self,
reservation: Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Result<Model<T>> {
let app = self
.app
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut app = app.borrow_mut();
Ok(app.insert_model(reservation, build_model))
}
fn update_model<T: 'static, R>(
&mut self,
handle: &Model<T>,
@@ -278,6 +300,19 @@ impl Context for AsyncWindowContext {
self.window.update(self, |_, cx| cx.new_model(build_model))
}
fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
self.window.update(self, |_, cx| cx.reserve_model())
}
fn insert_model<T: 'static>(
&mut self,
reservation: Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
self.window
.update(self, |_, cx| cx.insert_model(reservation, build_model))
}
fn update_model<T: 'static, R>(
&mut self,
handle: &Model<T>,
@@ -287,13 +322,6 @@ impl Context for AsyncWindowContext {
.update(self, |_, cx| cx.update_model(handle, update))
}
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
{
self.app.update_window(window, update)
}
fn read_model<T, R>(
&self,
handle: &Model<T>,
@@ -305,6 +333,13 @@ impl Context for AsyncWindowContext {
self.app.read_model(handle, read)
}
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
{
self.app.update_window(window, update)
}
fn read_window<T, R>(
&self,
window: &WindowHandle<T>,
+10 -2
View File
@@ -9,6 +9,7 @@ use std::{
hash::{Hash, Hasher},
marker::PhantomData,
mem,
num::NonZeroU64,
sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak,
@@ -31,6 +32,11 @@ impl From<u64> for EntityId {
}
impl EntityId {
/// Converts this entity id to a [NonZeroU64]
pub fn as_non_zero_u64(self) -> NonZeroU64 {
NonZeroU64::new(self.0.as_ffi()).unwrap()
}
/// Converts this entity id to a [u64]
pub fn as_u64(self) -> u64 {
self.0.as_ffi()
@@ -128,14 +134,16 @@ impl EntityMap {
dropped_entity_ids
.into_iter()
.map(|entity_id| {
.filter_map(|entity_id| {
let count = ref_counts.counts.remove(entity_id).unwrap();
debug_assert_eq!(
count.load(SeqCst),
0,
"dropped an entity that was referenced"
);
(entity_id, self.entities.remove(entity_id).unwrap())
// If the EntityId was allocated with `Context::reserve`,
// the entity may not have been inserted.
Some((entity_id, self.entities.remove(entity_id)?))
})
.collect()
}
+21 -8
View File
@@ -1,6 +1,7 @@
use crate::{
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
EventEmitter, Model, Reservation, Subscription, Task, View, WeakModel, WindowContext,
WindowHandle,
};
use anyhow::Result;
use derive_more::{Deref, DerefMut};
@@ -229,6 +230,18 @@ impl<'a, T> Context for ModelContext<'a, T> {
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>,
@@ -237,13 +250,6 @@ impl<'a, T> Context for ModelContext<'a, T> {
self.app.update_model(handle, update)
}
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_model<U, R>(
&self,
handle: &Model<U>,
@@ -255,6 +261,13 @@ impl<'a, T> Context for ModelContext<'a, T> {
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>,
+26
View File
@@ -42,6 +42,20 @@ impl Context for TestAppContext {
app.new_model(build_model)
}
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
let mut app = self.app.borrow_mut();
app.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
let mut app = self.app.borrow_mut();
app.insert_model(reservation, build_model)
}
fn update_model<T: 'static, R>(
&mut self,
handle: &Model<T>,
@@ -803,6 +817,18 @@ impl Context for VisualTestContext {
self.cx.new_model(build_model)
}
fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
self.cx.reserve_model()
}
fn insert_model<T: 'static>(
&mut self,
reservation: crate::Reservation<T>,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>> {
self.cx.insert_model(reservation, build_model)
}
fn update_model<T, R>(
&mut self,
handle: &Model<T>,