Merge pull request #2405 from zed-industries/fewer-context-traits

Antonio Scandurra created

Simplify traits for accessing app state uniformly across different kinds of contexts

Change summary

crates/editor/src/editor.rs                   |   6 
crates/editor/src/editor_tests.rs             |  12 
crates/editor/src/items.rs                    |   2 
crates/editor/src/test/editor_test_context.rs |   2 
crates/gpui/src/app.rs                        | 597 +++++++-------------
crates/gpui/src/app/test_app_context.rs       |  91 +--
crates/gpui/src/app/window.rs                 | 109 +--
crates/project/src/project.rs                 |   6 
crates/vim/src/test/vim_test_context.rs       |   4 
crates/workspace/src/dock.rs                  |  23 
crates/workspace/src/pane.rs                  |   4 
crates/workspace/src/persistence/model.rs     |   5 
crates/workspace/src/workspace.rs             |  12 
13 files changed, 319 insertions(+), 554 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -2736,7 +2736,7 @@ impl Editor {
         title: String,
         mut cx: AsyncAppContext,
     ) -> Result<()> {
-        let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
+        let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
 
         let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
         entries.sort_unstable_by_key(|(buffer, _)| {
@@ -2753,7 +2753,7 @@ impl Editor {
                         .buffer()
                         .read(cx)
                         .excerpt_containing(editor.selections.newest_anchor().head(), cx)
-                });
+                })?;
                 if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
                     if excerpted_buffer == *buffer {
                         let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
@@ -5835,7 +5835,7 @@ impl Editor {
                     buffer_highlights
                         .next()
                         .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
-                })
+                })?
             };
             if let Some(rename_range) = rename_range {
                 let rename_buffer_range = rename_range.to_offset(&snapshot);

crates/editor/src/editor_tests.rs 🔗

@@ -5673,8 +5673,8 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
         .await
         .unwrap();
     assert_eq!(
-        follower_1.read_with(cx, Editor::text),
-        leader.read_with(cx, Editor::text)
+        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
+        leader.read_with(cx, |editor, cx| editor.text(cx))
     );
     update_message.borrow_mut().take();
 
@@ -5697,8 +5697,8 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
         .await
         .unwrap();
     assert_eq!(
-        follower_2.read_with(cx, Editor::text),
-        leader.read_with(cx, Editor::text)
+        follower_2.read_with(cx, |editor, cx| editor.text(cx)),
+        leader.read_with(cx, |editor, cx| editor.text(cx))
     );
 
     // Remove some excerpts.
@@ -5725,8 +5725,8 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
         .unwrap();
     update_message.borrow_mut().take();
     assert_eq!(
-        follower_1.read_with(cx, Editor::text),
-        leader.read_with(cx, Editor::text)
+        follower_1.read_with(cx, |editor, cx| editor.text(cx)),
+        leader.read_with(cx, |editor, cx| editor.text(cx))
     );
 }
 

crates/editor/src/items.rs 🔗

@@ -78,7 +78,7 @@ impl FollowableItem for Editor {
                             == editor.read(cx).buffer.read(cx).as_singleton().as_ref();
                     ids_match || singleton_buffer_matches
                 })
-            });
+            })?;
 
             let editor = if let Some(editor) = editor {
                 editor

crates/editor/src/test/editor_test_context.rs 🔗

@@ -57,7 +57,7 @@ impl<'a> EditorTestContext<'a> {
 
     pub fn editor<F, T>(&self, read: F) -> T
     where
-        F: FnOnce(&Editor, &AppContext) -> T,
+        F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
     {
         self.editor.read_with(self.cx, read)
     }

crates/gpui/src/app.rs 🔗

@@ -121,75 +121,29 @@ pub trait View: Entity + Sized {
     }
 }
 
-pub trait ReadModel {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T;
+pub trait BorrowAppContext {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T;
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T;
 }
 
-pub trait ReadModelWith {
-    fn read_model_with<E: Entity, T>(
-        &self,
-        handle: &ModelHandle<E>,
-        read: &mut dyn FnMut(&E, &AppContext) -> T,
-    ) -> T;
-}
-
-pub trait UpdateModel {
-    fn update_model<T: Entity, O>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
-    ) -> O;
-}
-
-pub trait UpgradeModelHandle {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>>;
-
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool;
+pub trait BorrowWindowContext {
+    type ReturnValue<T>;
 
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle>;
-}
-
-pub trait UpgradeViewHandle {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>>;
-
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle>;
-}
-
-pub trait ReadView {
-    fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T;
-}
-
-pub trait ReadViewWith {
-    fn read_view_with<V, T>(
+    fn read_with<T, F: FnOnce(&WindowContext) -> T>(
         &self,
-        handle: &ViewHandle<V>,
-        read: &mut dyn FnMut(&V, &AppContext) -> T,
-    ) -> T
-    where
-        V: View;
-}
-
-pub trait UpdateView {
-    type Output<S>;
-
-    fn update_view<T, S>(
+        window_id: usize,
+        f: F,
+    ) -> Self::ReturnValue<T>;
+    fn update<T, F: FnOnce(&mut WindowContext) -> T>(
         &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> Self::Output<S>
-    where
-        T: View;
+        window_id: usize,
+        f: F,
+    ) -> Self::ReturnValue<T>;
 }
 
 #[derive(Clone)]
 pub struct App(Rc<RefCell<AppContext>>);
 
-#[derive(Clone)]
-pub struct AsyncAppContext(Rc<RefCell<AppContext>>);
-
 impl App {
     pub fn new(asset_source: impl AssetSource) -> Result<Self> {
         let platform = platform::current::platform();
@@ -336,6 +290,9 @@ impl App {
     }
 }
 
+#[derive(Clone)]
+pub struct AsyncAppContext(Rc<RefCell<AppContext>>);
+
 impl AsyncAppContext {
     pub fn spawn<F, Fut, T>(&self, f: F) -> Task<T>
     where
@@ -414,88 +371,38 @@ impl AsyncAppContext {
     }
 }
 
-impl UpdateModel for AsyncAppContext {
-    fn update_model<E: Entity, O>(
-        &mut self,
-        handle: &ModelHandle<E>,
-        update: &mut dyn FnMut(&mut E, &mut ModelContext<E>) -> O,
-    ) -> O {
-        self.0.borrow_mut().update_model(handle, update)
+impl BorrowAppContext for AsyncAppContext {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        self.0.borrow().read_with(f)
     }
-}
 
-impl UpgradeModelHandle for AsyncAppContext {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        self.0.borrow().upgrade_model_handle(handle)
-    }
-
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.0.borrow().model_handle_is_upgradable(handle)
-    }
-
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        self.0.borrow().upgrade_any_model_handle(handle)
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        self.0.borrow_mut().update(f)
     }
 }
 
-impl UpgradeViewHandle for AsyncAppContext {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
-        self.0.borrow_mut().upgrade_view_handle(handle)
-    }
-
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        self.0.borrow_mut().upgrade_any_view_handle(handle)
-    }
-}
+impl BorrowWindowContext for AsyncAppContext {
+    type ReturnValue<T> = Result<T>;
 
-impl ReadModelWith for AsyncAppContext {
-    fn read_model_with<E: Entity, T>(
-        &self,
-        handle: &ModelHandle<E>,
-        read: &mut dyn FnMut(&E, &AppContext) -> T,
-    ) -> T {
-        let cx = self.0.borrow();
-        let cx = &*cx;
-        read(handle.read(cx), cx)
+    fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> Result<T> {
+        self.0
+            .borrow()
+            .read_window(window_id, f)
+            .ok_or_else(|| anyhow!("window was closed"))
     }
-}
-
-impl UpdateView for AsyncAppContext {
-    type Output<S> = Result<S>;
 
-    fn update_view<T, S>(
+    fn update<T, F: FnOnce(&mut WindowContext) -> T>(
         &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> Result<S>
-    where
-        T: View,
-    {
+        window_id: usize,
+        f: F,
+    ) -> Result<T> {
         self.0
             .borrow_mut()
-            .update_window(handle.window_id, |cx| cx.update_view(handle, update))
+            .update_window(window_id, f)
             .ok_or_else(|| anyhow!("window was closed"))
     }
 }
 
-impl ReadViewWith for AsyncAppContext {
-    fn read_view_with<V, T>(
-        &self,
-        handle: &ViewHandle<V>,
-        read: &mut dyn FnMut(&V, &AppContext) -> T,
-    ) -> T
-    where
-        V: View,
-    {
-        let cx = self.0.borrow();
-        let cx = &*cx;
-        read(handle.read(cx), cx)
-    }
-}
-
 type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut WindowContext, usize);
 type GlobalActionCallback = dyn FnMut(&dyn Action, &mut AppContext);
 
@@ -1289,6 +1196,67 @@ impl AppContext {
         })
     }
 
+    pub fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
+        if let Some(model) = self.models.get(&handle.model_id) {
+            model
+                .as_any()
+                .downcast_ref()
+                .expect("downcast is type safe")
+        } else {
+            panic!("circular model reference");
+        }
+    }
+
+    fn update_model<T: Entity, V>(
+        &mut self,
+        handle: &ModelHandle<T>,
+        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V,
+    ) -> V {
+        if let Some(mut model) = self.models.remove(&handle.model_id) {
+            self.update(|this| {
+                let mut cx = ModelContext::new(this, handle.model_id);
+                let result = update(
+                    model
+                        .as_any_mut()
+                        .downcast_mut()
+                        .expect("downcast is type safe"),
+                    &mut cx,
+                );
+                this.models.insert(handle.model_id, model);
+                result
+            })
+        } else {
+            panic!("circular model update");
+        }
+    }
+
+    fn upgrade_model_handle<T: Entity>(
+        &self,
+        handle: &WeakModelHandle<T>,
+    ) -> Option<ModelHandle<T>> {
+        if self.ref_counts.lock().is_entity_alive(handle.model_id) {
+            Some(ModelHandle::new(handle.model_id, &self.ref_counts))
+        } else {
+            None
+        }
+    }
+
+    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+        self.ref_counts.lock().is_entity_alive(handle.model_id)
+    }
+
+    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
+        if self.ref_counts.lock().is_entity_alive(handle.model_id) {
+            Some(AnyModelHandle::new(
+                handle.model_id,
+                handle.model_type,
+                self.ref_counts.clone(),
+            ))
+        } else {
+            None
+        }
+    }
+
     pub fn add_window<V, F>(
         &mut self,
         window_options: WindowOptions,
@@ -1444,6 +1412,39 @@ impl AppContext {
         .unwrap()
     }
 
+    pub fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
+        if let Some(view) = self.views.get(&(handle.window_id, handle.view_id)) {
+            view.as_any().downcast_ref().expect("downcast is type safe")
+        } else {
+            panic!("circular view reference for type {}", type_name::<T>());
+        }
+    }
+
+    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
+        if self.ref_counts.lock().is_entity_alive(handle.view_id) {
+            Some(ViewHandle::new(
+                handle.window_id,
+                handle.view_id,
+                &self.ref_counts,
+            ))
+        } else {
+            None
+        }
+    }
+
+    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
+        if self.ref_counts.lock().is_entity_alive(handle.view_id) {
+            Some(AnyViewHandle::new(
+                handle.window_id,
+                handle.view_id,
+                handle.view_type,
+                self.ref_counts.clone(),
+            ))
+        } else {
+            None
+        }
+    }
+
     fn remove_dropped_entities(&mut self) {
         loop {
             let (dropped_models, dropped_views, dropped_element_states) =
@@ -2078,107 +2079,13 @@ impl AppContext {
     }
 }
 
-impl ReadModel for AppContext {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        if let Some(model) = self.models.get(&handle.model_id) {
-            model
-                .as_any()
-                .downcast_ref()
-                .expect("downcast is type safe")
-        } else {
-            panic!("circular model reference");
-        }
-    }
-}
-
-impl UpdateModel for AppContext {
-    fn update_model<T: Entity, V>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V,
-    ) -> V {
-        if let Some(mut model) = self.models.remove(&handle.model_id) {
-            self.update(|this| {
-                let mut cx = ModelContext::new(this, handle.model_id);
-                let result = update(
-                    model
-                        .as_any_mut()
-                        .downcast_mut()
-                        .expect("downcast is type safe"),
-                    &mut cx,
-                );
-                this.models.insert(handle.model_id, model);
-                result
-            })
-        } else {
-            panic!("circular model update");
-        }
-    }
-}
-
-impl UpgradeModelHandle for AppContext {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        if self.ref_counts.lock().is_entity_alive(handle.model_id) {
-            Some(ModelHandle::new(handle.model_id, &self.ref_counts))
-        } else {
-            None
-        }
+impl BorrowAppContext for AppContext {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        f(self)
     }
 
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.ref_counts.lock().is_entity_alive(handle.model_id)
-    }
-
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        if self.ref_counts.lock().is_entity_alive(handle.model_id) {
-            Some(AnyModelHandle::new(
-                handle.model_id,
-                handle.model_type,
-                self.ref_counts.clone(),
-            ))
-        } else {
-            None
-        }
-    }
-}
-
-impl UpgradeViewHandle for AppContext {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
-        if self.ref_counts.lock().is_entity_alive(handle.view_id) {
-            Some(ViewHandle::new(
-                handle.window_id,
-                handle.view_id,
-                &self.ref_counts,
-            ))
-        } else {
-            None
-        }
-    }
-
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        if self.ref_counts.lock().is_entity_alive(handle.view_id) {
-            Some(AnyViewHandle::new(
-                handle.window_id,
-                handle.view_id,
-                handle.view_type,
-                self.ref_counts.clone(),
-            ))
-        } else {
-            None
-        }
-    }
-}
-
-impl ReadView for AppContext {
-    fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
-        if let Some(view) = self.views.get(&(handle.window_id, handle.view_id)) {
-            view.as_any().downcast_ref().expect("downcast is type safe")
-        } else {
-            panic!("circular view reference for type {}", type_name::<T>());
-        }
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        f(self)
     }
 }
 
@@ -2867,36 +2774,13 @@ impl<M> AsMut<AppContext> for ModelContext<'_, M> {
     }
 }
 
-impl<M> ReadModel for ModelContext<'_, M> {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        self.app.read_model(handle)
+impl<M> BorrowAppContext for ModelContext<'_, M> {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        self.app.read_with(f)
     }
-}
 
-impl<M> UpdateModel for ModelContext<'_, M> {
-    fn update_model<T: Entity, V>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> V,
-    ) -> V {
-        self.app.update_model(handle, update)
-    }
-}
-
-impl<M> UpgradeModelHandle for ModelContext<'_, M> {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        self.app.upgrade_model_handle(handle)
-    }
-
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.app.model_handle_is_upgradable(handle)
-    }
-
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        self.app.upgrade_any_model_handle(handle)
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        self.app.update(f)
     }
 }
 
@@ -3445,67 +3329,25 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
     }
 }
 
-impl<V> UpgradeModelHandle for ViewContext<'_, '_, V> {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        self.window_context.upgrade_model_handle(handle)
-    }
-
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.window_context.model_handle_is_upgradable(handle)
-    }
-
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        self.window_context.upgrade_any_model_handle(handle)
-    }
-}
-
-impl<V> UpgradeViewHandle for ViewContext<'_, '_, V> {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
-        self.window_context.upgrade_view_handle(handle)
+impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        BorrowAppContext::read_with(&*self.window_context, f)
     }
 
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        self.window_context.upgrade_any_view_handle(handle)
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        BorrowAppContext::update(&mut *self.window_context, f)
     }
 }
 
-impl<V: View> ReadModel for ViewContext<'_, '_, V> {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        self.window_context.read_model(handle)
-    }
-}
+impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
+    type ReturnValue<T> = T;
 
-impl<V: View> UpdateModel for ViewContext<'_, '_, V> {
-    fn update_model<T: Entity, O>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
-    ) -> O {
-        self.window_context.update_model(handle, update)
+    fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
+        BorrowWindowContext::read_with(&*self.window_context, window_id, f)
     }
-}
-
-impl<V: View> ReadView for ViewContext<'_, '_, V> {
-    fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
-        self.window_context.read_view(handle)
-    }
-}
 
-impl<V: View> UpdateView for ViewContext<'_, '_, V> {
-    type Output<S> = S;
-
-    fn update_view<T, S>(
-        &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> S
-    where
-        T: View,
-    {
-        self.window_context.update_view(handle, update)
+    fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
+        BorrowWindowContext::update(&mut *self.window_context, window_id, f)
     }
 }
 
@@ -3541,61 +3383,25 @@ impl<V: View> DerefMut for EventContext<'_, '_, '_, V> {
     }
 }
 
-impl<V: View> UpdateModel for EventContext<'_, '_, '_, V> {
-    fn update_model<T: Entity, O>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
-    ) -> O {
-        self.view_context.update_model(handle, update)
-    }
-}
-
-impl<V: View> ReadView for EventContext<'_, '_, '_, V> {
-    fn read_view<W: View>(&self, handle: &crate::ViewHandle<W>) -> &W {
-        self.view_context.read_view(handle)
+impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        BorrowAppContext::read_with(&*self.view_context, f)
     }
-}
 
-impl<V: View> UpdateView for EventContext<'_, '_, '_, V> {
-    type Output<S> = S;
-
-    fn update_view<T, S>(
-        &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> S
-    where
-        T: View,
-    {
-        self.view_context.update_view(handle, update)
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        BorrowAppContext::update(&mut *self.view_context, f)
     }
 }
 
-impl<V: View> UpgradeModelHandle for EventContext<'_, '_, '_, V> {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        self.view_context.upgrade_model_handle(handle)
-    }
-
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.view_context.model_handle_is_upgradable(handle)
-    }
-
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        self.view_context.upgrade_any_model_handle(handle)
-    }
-}
+impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
+    type ReturnValue<T> = T;
 
-impl<V: View> UpgradeViewHandle for EventContext<'_, '_, '_, V> {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
-        self.view_context.upgrade_view_handle(handle)
+    fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
+        BorrowWindowContext::read_with(&*self.view_context, window_id, f)
     }
 
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        self.view_context.upgrade_any_view_handle(handle)
+    fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
+        BorrowWindowContext::update(&mut *self.view_context, window_id, f)
     }
 }
 
@@ -3703,31 +3509,29 @@ impl<T: Entity> ModelHandle<T> {
         self.model_id
     }
 
-    pub fn read<'a, C: ReadModel>(&self, cx: &'a C) -> &'a T {
+    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
         cx.read_model(self)
     }
 
     pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> S
     where
-        C: ReadModelWith,
+        C: BorrowAppContext,
         F: FnOnce(&T, &AppContext) -> S,
     {
-        let mut read = Some(read);
-        cx.read_model_with(self, &mut |model, cx| {
-            let read = read.take().unwrap();
-            read(model, cx)
-        })
+        cx.read_with(|cx| read(self.read(cx), cx))
     }
 
     pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> S
     where
-        C: UpdateModel,
+        C: BorrowAppContext,
         F: FnOnce(&mut T, &mut ModelContext<T>) -> S,
     {
         let mut update = Some(update);
-        cx.update_model(self, &mut |model, cx| {
-            let update = update.take().unwrap();
-            update(model, cx)
+        cx.update(|cx| {
+            cx.update_model(self, &mut |model, cx| {
+                let update = update.take().unwrap();
+                update(model, cx)
+            })
         })
     }
 }
@@ -3841,12 +3645,12 @@ impl<T: Entity> WeakModelHandle<T> {
         self.model_id
     }
 
-    pub fn is_upgradable(&self, cx: &impl UpgradeModelHandle) -> bool {
-        cx.model_handle_is_upgradable(self)
+    pub fn is_upgradable(&self, cx: &impl BorrowAppContext) -> bool {
+        cx.read_with(|cx| cx.model_handle_is_upgradable(self))
     }
 
-    pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<T>> {
-        cx.upgrade_model_handle(self)
+    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<ModelHandle<T>> {
+        cx.read_with(|cx| cx.upgrade_model_handle(self))
     }
 }
 
@@ -3924,31 +3728,33 @@ impl<T: View> ViewHandle<T> {
         self.view_id
     }
 
-    pub fn read<'a, C: ReadView>(&self, cx: &'a C) -> &'a T {
+    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
         cx.read_view(self)
     }
 
-    pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> S
+    pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::ReturnValue<S>
     where
-        C: ReadViewWith,
-        F: FnOnce(&T, &AppContext) -> S,
+        C: BorrowWindowContext,
+        F: FnOnce(&T, &ViewContext<T>) -> S,
     {
-        let mut read = Some(read);
-        cx.read_view_with(self, &mut |view, cx| {
-            let read = read.take().unwrap();
-            read(view, cx)
+        cx.read_with(self.window_id, |cx| {
+            let cx = ViewContext::immutable(cx, self.view_id);
+            read(cx.read_view(self), &cx)
         })
     }
 
-    pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Output<S>
+    pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::ReturnValue<S>
     where
-        C: UpdateView,
+        C: BorrowWindowContext,
         F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
     {
         let mut update = Some(update);
-        cx.update_view(self, &mut |view, cx| {
-            let update = update.take().unwrap();
-            update(view, cx)
+
+        cx.update(self.window_id, |cx| {
+            cx.update_view(self, &mut |view, cx| {
+                let update = update.take().unwrap();
+                update(view, cx)
+            })
         })
     }
 
@@ -4222,9 +4028,10 @@ pub struct AnyWeakModelHandle {
 }
 
 impl AnyWeakModelHandle {
-    pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<AnyModelHandle> {
-        cx.upgrade_any_model_handle(self)
+    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<AnyModelHandle> {
+        cx.read_with(|cx| cx.upgrade_any_model_handle(self))
     }
+
     pub fn model_type(&self) -> TypeId {
         self.model_type
     }
@@ -4259,13 +4066,13 @@ impl<T> WeakHandle for WeakViewHandle<T> {
     }
 }
 
-impl<T: View> WeakViewHandle<T> {
+impl<V: View> WeakViewHandle<V> {
     fn new(window_id: usize, view_id: usize) -> Self {
         Self {
             any_handle: AnyWeakViewHandle {
                 window_id,
                 view_id,
-                view_type: TypeId::of::<T>(),
+                view_type: TypeId::of::<V>(),
             },
             view_type: PhantomData,
         }
@@ -4283,8 +4090,20 @@ impl<T: View> WeakViewHandle<T> {
         self.any_handle
     }
 
-    pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option<ViewHandle<T>> {
-        cx.upgrade_view_handle(self)
+    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<ViewHandle<V>> {
+        cx.read_with(|cx| cx.upgrade_view_handle(self))
+    }
+
+    pub fn update<T>(
+        &self,
+        cx: &mut impl BorrowAppContext,
+        update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
+    ) -> Option<T> {
+        cx.update(|cx| {
+            let handle = cx.upgrade_view_handle(self)?;
+
+            cx.update_window(self.window_id, |cx| handle.update(cx, update))
+        })
     }
 }
 
@@ -4331,8 +4150,8 @@ impl AnyWeakViewHandle {
         self.view_id
     }
 
-    pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option<AnyViewHandle> {
-        cx.upgrade_any_view_handle(self)
+    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<AnyViewHandle> {
+        cx.read_with(|cx| cx.upgrade_any_view_handle(self))
     }
 }
 

crates/gpui/src/app/test_app_context.rs 🔗

@@ -1,3 +1,18 @@
+use crate::{
+    executor,
+    geometry::vector::Vector2F,
+    keymap_matcher::Keystroke,
+    platform,
+    platform::{Event, InputHandler, KeyDownEvent, Platform},
+    Action, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
+    Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
+    WeakHandle, WindowContext,
+};
+use collections::BTreeMap;
+use futures::Future;
+use itertools::Itertools;
+use parking_lot::{Mutex, RwLock};
+use smol::stream::StreamExt;
 use std::{
     any::Any,
     cell::RefCell,
@@ -11,23 +26,6 @@ use std::{
     time::Duration,
 };
 
-use futures::Future;
-use itertools::Itertools;
-use parking_lot::{Mutex, RwLock};
-use smol::stream::StreamExt;
-
-use crate::{
-    executor,
-    geometry::vector::Vector2F,
-    keymap_matcher::Keystroke,
-    platform,
-    platform::{Event, InputHandler, KeyDownEvent, Platform},
-    Action, AnyViewHandle, AppContext, Entity, FontCache, Handle, ModelContext, ModelHandle,
-    ReadModelWith, ReadViewWith, Subscription, Task, UpdateModel, UpdateView, View, ViewContext,
-    ViewHandle, WeakHandle, WindowContext,
-};
-use collections::BTreeMap;
-
 use super::{
     ref_counts::LeakDetector, window_input_handler::WindowInputHandler, AsyncAppContext, RefCounts,
 };
@@ -381,58 +379,31 @@ impl TestAppContext {
     }
 }
 
-impl UpdateModel for TestAppContext {
-    fn update_model<T: Entity, O>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> O,
-    ) -> O {
-        self.cx.borrow_mut().update_model(handle, update)
+impl BorrowAppContext for TestAppContext {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        self.cx.borrow().read_with(f)
     }
-}
 
-impl ReadModelWith for TestAppContext {
-    fn read_model_with<E: Entity, T>(
-        &self,
-        handle: &ModelHandle<E>,
-        read: &mut dyn FnMut(&E, &AppContext) -> T,
-    ) -> T {
-        let cx = self.cx.borrow();
-        let cx = &*cx;
-        read(handle.read(cx), cx)
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        self.cx.borrow_mut().update(f)
     }
 }
 
-impl UpdateView for TestAppContext {
-    type Output<S> = S;
+impl BorrowWindowContext for TestAppContext {
+    type ReturnValue<T> = T;
 
-    fn update_view<T, S>(
-        &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> S
-    where
-        T: View,
-    {
+    fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
         self.cx
-            .borrow_mut()
-            .update_window(handle.window_id, |cx| cx.update_view(handle, update))
-            .unwrap()
+            .borrow()
+            .read_window(window_id, f)
+            .expect("window was closed")
     }
-}
 
-impl ReadViewWith for TestAppContext {
-    fn read_view_with<V, T>(
-        &self,
-        handle: &ViewHandle<V>,
-        read: &mut dyn FnMut(&V, &AppContext) -> T,
-    ) -> T
-    where
-        V: View,
-    {
-        let cx = self.cx.borrow();
-        let cx = &*cx;
-        read(handle.read(cx), cx)
+    fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
+        self.cx
+            .borrow_mut()
+            .update_window(window_id, f)
+            .expect("window was closed")
     }
 }
 

crates/gpui/src/app/window.rs 🔗

@@ -13,11 +13,9 @@ use crate::{
     },
     text_layout::TextLayoutCache,
     util::post_inc,
-    Action, AnyModelHandle, AnyView, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle,
-    AppContext, Effect, Element, Entity, Handle, ModelContext, ModelHandle, MouseRegion,
-    MouseRegionId, ParentId, ReadModel, ReadView, SceneBuilder, Subscription, UpdateModel,
-    UpdateView, UpgradeModelHandle, UpgradeViewHandle, View, ViewContext, ViewHandle,
-    WeakModelHandle, WeakViewHandle, WindowInvalidation,
+    Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
+    Element, Entity, Handle, MouseRegion, MouseRegionId, ParentId, SceneBuilder, Subscription,
+    View, ViewContext, ViewHandle, WindowInvalidation,
 };
 use anyhow::{anyhow, bail, Result};
 use collections::{HashMap, HashSet};
@@ -133,76 +131,33 @@ impl DerefMut for WindowContext<'_> {
     }
 }
 
-impl ReadModel for WindowContext<'_> {
-    fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
-        self.app_context.read_model(handle)
+impl BorrowAppContext for WindowContext<'_> {
+    fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
+        self.app_context.read_with(f)
     }
-}
-
-impl UpdateModel for WindowContext<'_> {
-    fn update_model<T: Entity, R>(
-        &mut self,
-        handle: &ModelHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ModelContext<T>) -> R,
-    ) -> R {
-        self.app_context.update_model(handle, update)
-    }
-}
-
-impl ReadView for WindowContext<'_> {
-    fn read_view<W: View>(&self, handle: &crate::ViewHandle<W>) -> &W {
-        self.app_context.read_view(handle)
-    }
-}
-
-impl UpdateView for WindowContext<'_> {
-    type Output<S> = S;
 
-    fn update_view<T, S>(
-        &mut self,
-        handle: &ViewHandle<T>,
-        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-    ) -> S
-    where
-        T: View,
-    {
-        self.update_any_view(handle.view_id, |view, cx| {
-            let mut cx = ViewContext::mutable(cx, handle.view_id);
-            update(
-                view.as_any_mut()
-                    .downcast_mut()
-                    .expect("downcast is type safe"),
-                &mut cx,
-            )
-        })
-        .expect("view is already on the stack")
+    fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
+        self.app_context.update(f)
     }
 }
 
-impl UpgradeModelHandle for WindowContext<'_> {
-    fn upgrade_model_handle<T: Entity>(
-        &self,
-        handle: &WeakModelHandle<T>,
-    ) -> Option<ModelHandle<T>> {
-        self.app_context.upgrade_model_handle(handle)
-    }
-
-    fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
-        self.app_context.model_handle_is_upgradable(handle)
-    }
+impl BorrowWindowContext for WindowContext<'_> {
+    type ReturnValue<T> = T;
 
-    fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
-        self.app_context.upgrade_any_model_handle(handle)
-    }
-}
-
-impl UpgradeViewHandle for WindowContext<'_> {
-    fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
-        self.app_context.upgrade_view_handle(handle)
+    fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
+        if self.window_id == window_id {
+            f(self)
+        } else {
+            panic!("read_with called with id of window that does not belong to this context")
+        }
     }
 
-    fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option<AnyViewHandle> {
-        self.app_context.upgrade_any_view_handle(handle)
+    fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
+        if self.window_id == window_id {
+            f(self)
+        } else {
+            panic!("update called with id of window that does not belong to this context")
+        }
     }
 }
 
@@ -266,6 +221,26 @@ impl<'a> WindowContext<'a> {
         Some(result)
     }
 
+    pub(crate) fn update_view<T, S>(
+        &mut self,
+        handle: &ViewHandle<T>,
+        update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
+    ) -> S
+    where
+        T: View,
+    {
+        self.update_any_view(handle.view_id, |view, cx| {
+            let mut cx = ViewContext::mutable(cx, handle.view_id);
+            update(
+                view.as_any_mut()
+                    .downcast_mut()
+                    .expect("downcast is type safe"),
+                &mut cx,
+            )
+        })
+        .expect("view is already on the stack")
+    }
+
     pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut WindowContext)) {
         let window_id = self.window_id;
         self.app_context.defer(move |cx| {

crates/project/src/project.rs 🔗

@@ -19,8 +19,8 @@ use futures::{
     AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt,
 };
 use gpui::{
-    AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task,
-    UpgradeModelHandle, WeakModelHandle,
+    AnyModelHandle, AppContext, AsyncAppContext, BorrowAppContext, Entity, ModelContext,
+    ModelHandle, Task, WeakModelHandle,
 };
 use language::{
     point_to_lsp,
@@ -6418,7 +6418,7 @@ impl WorktreeHandle {
 }
 
 impl OpenBuffer {
-    pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<Buffer>> {
+    pub fn upgrade(&self, cx: &impl BorrowAppContext) -> Option<ModelHandle<Buffer>> {
         match self {
             OpenBuffer::Strong(handle) => Some(handle.clone()),
             OpenBuffer::Weak(handle) => handle.upgrade(cx),

crates/vim/src/test/vim_test_context.rs 🔗

@@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut};
 use editor::test::{
     editor_lsp_test_context::EditorLspTestContext, editor_test_context::EditorTestContext,
 };
-use gpui::{AppContext, ContextHandle};
+use gpui::ContextHandle;
 use search::{BufferSearchBar, ProjectSearchBar};
 
 use crate::{state::Operator, *};
@@ -45,7 +45,7 @@ impl<'a> VimTestContext<'a> {
 
     pub fn workspace<F, T>(&mut self, read: F) -> T
     where
-        F: FnOnce(&Workspace, &AppContext) -> T,
+        F: FnOnce(&Workspace, &ViewContext<Workspace>) -> T,
     {
         self.cx.workspace.read_with(self.cx.cx.cx, read)
     }

crates/workspace/src/dock.rs 🔗

@@ -428,7 +428,7 @@ mod tests {
         path::PathBuf,
     };
 
-    use gpui::{AppContext, TestAppContext, UpdateView, View, ViewContext};
+    use gpui::{AppContext, BorrowWindowContext, TestAppContext, ViewContext, WindowContext};
     use project::{FakeFs, Project};
     use settings::Settings;
 
@@ -660,7 +660,7 @@ mod tests {
 
         pub fn workspace<F, T>(&self, read: F) -> T
         where
-            F: FnOnce(&Workspace, &AppContext) -> T,
+            F: FnOnce(&Workspace, &ViewContext<Workspace>) -> T,
         {
             self.workspace.read_with(self.cx, read)
         }
@@ -810,18 +810,15 @@ mod tests {
         }
     }
 
-    impl<'a> UpdateView for DockTestContext<'a> {
-        type Output<S> = S;
+    impl BorrowWindowContext for DockTestContext<'_> {
+        type ReturnValue<T> = T;
 
-        fn update_view<T, S>(
-            &mut self,
-            handle: &ViewHandle<T>,
-            update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
-        ) -> S
-        where
-            T: View,
-        {
-            handle.update(self.cx, update)
+        fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
+            BorrowWindowContext::read_with(self.cx, window_id, f)
+        }
+
+        fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
+            BorrowWindowContext::update(self.cx, window_id, f)
         }
     }
 }

crates/workspace/src/pane.rs 🔗

@@ -981,7 +981,7 @@ impl Pane {
                 // was started.
                 let (item_ix, mut project_item_ids) = pane.read_with(&cx, |pane, cx| {
                     (pane.index_for_item(&*item), item.project_item_model_ids(cx))
-                });
+                })?;
                 let item_ix = if let Some(ix) = item_ix {
                     ix
                 } else {
@@ -1001,7 +1001,7 @@ impl Pane {
                             project_item_ids.retain(|id| !other_project_item_ids.contains(id));
                         }
                     }
-                });
+                })?;
                 let should_save = project_item_ids
                     .iter()
                     .any(|id| saved_project_items_ids.insert(*id));

crates/workspace/src/persistence/model.rs 🔗

@@ -140,7 +140,10 @@ impl SerializedPaneGroup {
                     .await
                     .log_err()?;
 
-                if pane.read_with(cx, |pane, _| pane.items_len() != 0) {
+                if pane
+                    .read_with(cx, |pane, _| pane.items_len() != 0)
+                    .log_err()?
+                {
                     Some((Member::Pane(pane.clone()), active.then(|| pane)))
                 } else {
                     workspace

crates/workspace/src/workspace.rs 🔗

@@ -1226,7 +1226,7 @@ impl Workspace {
                     cx.read(|cx| (item.is_singleton(cx), item.project_entry_ids(cx)));
                 if singleton || !project_entry_ids.is_empty() {
                     if let Some(ix) =
-                        pane.read_with(&cx, |pane, _| pane.index_for_item(item.as_ref()))
+                        pane.read_with(&cx, |pane, _| pane.index_for_item(item.as_ref()))?
                     {
                         if !Pane::save_item(
                             project.clone(),
@@ -2298,7 +2298,7 @@ impl Workspace {
         this.read_with(&cx, |this, _| {
             this.leader_updates_tx
                 .unbounded_send((leader_id, envelope.payload))
-        })?;
+        })??;
         Ok(())
     }
 
@@ -2354,7 +2354,7 @@ impl Workspace {
                         .flat_map(|states_by_pane| states_by_pane.keys())
                         .cloned()
                         .collect()
-                });
+                })?;
                 Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?;
             }
         }
@@ -2369,7 +2369,7 @@ impl Workspace {
         views: Vec<proto::View>,
         cx: &mut AsyncAppContext,
     ) -> Result<()> {
-        let project = this.read_with(cx, |this, _| this.project.clone());
+        let project = this.read_with(cx, |this, _| this.project.clone())?;
         let replica_id = project
             .read_with(cx, |project, _| {
                 project
@@ -2699,7 +2699,7 @@ impl Workspace {
                             workspace.dock_pane().clone(),
                             workspace.last_active_center_pane.clone(),
                         )
-                    });
+                    })?;
 
                 serialized_workspace
                     .dock_pane
@@ -2765,7 +2765,7 @@ impl Workspace {
                 })?;
 
                 // Serialize ourself to make sure our timestamps and any pane / item changes are replicated
-                workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))
+                workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))?
             }
             anyhow::Ok(())
         })