Make production code compile again

Antonio Scandurra created

Change summary

crates/breadcrumbs/src/breadcrumbs.rs         |   2 
crates/copilot/src/sign_in.rs                 |  33 
crates/diagnostics/src/diagnostics.rs         |   4 
crates/drag_and_drop/src/drag_and_drop.rs     |   8 
crates/editor/src/editor_tests.rs             | 525 +++++++++++---------
crates/editor/src/element.rs                  |  24 
crates/editor/src/test/editor_test_context.rs |   7 
crates/feedback/src/feedback_editor.rs        |  27 
crates/go_to_line/src/go_to_line.rs           |  14 
crates/gpui/src/app.rs                        | 166 +++---
crates/gpui/src/app/test_app_context.rs       |  25 
crates/gpui/src/app/window.rs                 |  69 ++
crates/gpui_macros/src/gpui_macros.rs         |  57 +
crates/outline/src/outline.rs                 |   8 
crates/vim/src/editor_events.rs               |  15 
crates/vim/src/vim.rs                         |  81 +--
crates/workspace/src/item.rs                  |  12 
crates/workspace/src/pane.rs                  |  12 
crates/workspace/src/searchable.rs            |  43 
crates/workspace/src/toolbar.rs               |  14 
crates/workspace/src/workspace.rs             |  34 
crates/zed/src/zed.rs                         |  37 
22 files changed, 674 insertions(+), 543 deletions(-)

Detailed changes

crates/breadcrumbs/src/breadcrumbs.rs 🔗

@@ -148,7 +148,7 @@ impl ToolbarItemView for Breadcrumbs {
         }
     }
 
-    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut gpui::AppContext) {
+    fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
         self.pane_focused = pane_focused;
     }
 }

crates/copilot/src/sign_in.rs 🔗

@@ -26,27 +26,31 @@ pub fn init(cx: &mut AppContext) {
 
         match &status {
             crate::Status::SigningIn { prompt } => {
-                if let Some(code_verification_handle) = code_verification.as_mut() {
-                    if cx.has_window(code_verification_handle.window_id()) {
-                        code_verification_handle.update(cx, |code_verification_view, cx| {
-                            code_verification_view.set_status(status, cx);
-                            cx.activate_window();
+                if let Some(code_verification) = code_verification.as_mut() {
+                    if cx.has_window(code_verification.window_id()) {
+                        cx.update_window(code_verification.window_id(), |cx| {
+                            code_verification.update(cx, |code_verification_view, cx| {
+                                code_verification_view.set_status(status, cx);
+                                cx.activate_window();
+                            });
                         });
                     } else {
-                        create_copilot_auth_window(cx, &status, &mut code_verification);
+                        *code_verification = create_copilot_auth_window(cx, &status);
                     }
                 } else if let Some(_prompt) = prompt {
-                    create_copilot_auth_window(cx, &status, &mut code_verification);
+                    code_verification = Some(create_copilot_auth_window(cx, &status));
                 }
             }
             Status::Authorized | Status::Unauthorized => {
                 if let Some(code_verification) = code_verification.as_ref() {
-                    code_verification.update(cx, |code_verification, cx| {
-                        code_verification.set_status(status, cx);
-                        cx.activate_window();
-                    });
+                    cx.update_window(code_verification.window_id(), |cx| {
+                        code_verification.update(cx, |code_verification, cx| {
+                            code_verification.set_status(status, cx);
+                            cx.activate_window();
+                        });
 
-                    cx.platform().activate(true);
+                        cx.platform().activate(true);
+                    });
                 }
             }
             _ => {
@@ -62,8 +66,7 @@ pub fn init(cx: &mut AppContext) {
 fn create_copilot_auth_window(
     cx: &mut AppContext,
     status: &Status,
-    code_verification: &mut Option<ViewHandle<CopilotCodeVerification>>,
-) {
+) -> ViewHandle<CopilotCodeVerification> {
     let window_size = cx.global::<Settings>().theme.copilot.modal.dimensions();
     let window_options = WindowOptions {
         bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
@@ -77,7 +80,7 @@ fn create_copilot_auth_window(
     let (_, view) = cx.add_window(window_options, |_cx| {
         CopilotCodeVerification::new(status.clone())
     });
-    *code_verification = Some(view);
+    view
 }
 
 pub struct CopilotCodeVerification {

crates/diagnostics/src/diagnostics.rs 🔗

@@ -765,7 +765,7 @@ mod tests {
         display_map::{BlockContext, TransformBlock},
         DisplayPoint,
     };
-    use gpui::TestAppContext;
+    use gpui::{TestAppContext, WindowContext};
     use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
     use serde_json::json;
     use unindent::Unindent as _;
@@ -1175,7 +1175,7 @@ mod tests {
         });
     }
 
-    fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut AppContext) -> Vec<(u32, String)> {
+    fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut WindowContext) -> Vec<(u32, String)> {
         editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
             snapshot

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -6,7 +6,7 @@ use gpui::{
     geometry::{rect::RectF, vector::Vector2F},
     platform::{CursorStyle, MouseButton},
     scene::{MouseDown, MouseDrag},
-    AppContext, Drawable, Element, View, ViewContext, WeakViewHandle,
+    Drawable, Element, View, ViewContext, WeakViewHandle, WindowContext,
 };
 
 const DEAD_ZONE: f32 = 4.;
@@ -263,7 +263,7 @@ impl<V: View> DragAndDrop<V> {
             })
     }
 
-    pub fn cancel_dragging<P: Any>(&mut self, cx: &mut AppContext) {
+    pub fn cancel_dragging<P: Any>(&mut self, cx: &mut WindowContext) {
         if let Some(State::Dragging {
             payload, window_id, ..
         }) = &self.currently_dragged
@@ -276,13 +276,13 @@ impl<V: View> DragAndDrop<V> {
         }
     }
 
-    fn finish_dragging(&mut self, cx: &mut AppContext) {
+    fn finish_dragging(&mut self, cx: &mut WindowContext) {
         if let Some(State::Dragging { window_id, .. }) = self.currently_dragged.take() {
             self.notify_containers_for_window(window_id, cx);
         }
     }
 
-    fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut AppContext) {
+    fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut WindowContext) {
         self.containers.retain(|container| {
             if let Some(container) = container.upgrade(cx) {
                 if container.window_id() == window_id {

crates/editor/src/editor_tests.rs 🔗

@@ -9,7 +9,7 @@ use gpui::{
     executor::Deterministic,
     geometry::{rect::RectF, vector::vec2f},
     platform::{WindowBounds, WindowOptions},
-    serde_json,
+    serde_json, TestAppContext,
 };
 use indoc::indoc;
 use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
@@ -28,8 +28,8 @@ use workspace::{
 };
 
 #[gpui::test]
-fn test_edit_events(cx: &mut AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_edit_events(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let buffer = cx.add_model(|cx| {
         let mut buffer = language::Buffer::new(0, "123456", cx);
         buffer.set_group_interval(Duration::from_secs(1));
@@ -37,7 +37,7 @@ fn test_edit_events(cx: &mut AppContext) {
     });
 
     let events = Rc::new(RefCell::new(Vec::new()));
-    let (_, editor1) = cx.add_window(Default::default(), {
+    let (_, editor1) = cx.add_window({
         let events = events.clone();
         |cx| {
             cx.subscribe(&cx.handle(), move |_, _, event, _| {
@@ -52,7 +52,7 @@ fn test_edit_events(cx: &mut AppContext) {
             Editor::for_buffer(buffer.clone(), None, cx)
         }
     });
-    let (_, editor2) = cx.add_window(Default::default(), {
+    let (_, editor2) = cx.add_window({
         let events = events.clone();
         |cx| {
             cx.subscribe(&cx.handle(), move |_, _, event, _| {
@@ -155,13 +155,13 @@ fn test_edit_events(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_undo_redo_with_selection_restoration(cx: &mut AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let mut now = Instant::now();
     let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
-    let group_interval = buffer.read(cx).transaction_group_interval();
+    let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+    let (_, editor) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
 
     editor.update(cx, |editor, cx| {
         editor.start_transaction_at(now, cx);
@@ -225,8 +225,8 @@ fn test_undo_redo_with_selection_restoration(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_ime_composition(cx: &mut AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_ime_composition(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let buffer = cx.add_model(|cx| {
         let mut buffer = language::Buffer::new(0, "abcde", cx);
         // Ensure automatic grouping doesn't occur.
@@ -235,7 +235,7 @@ fn test_ime_composition(cx: &mut AppContext) {
     });
 
     let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-    cx.add_window(Default::default(), |cx| {
+    cx.add_window(|cx| {
         let mut editor = build_editor(buffer.clone(), cx);
 
         // Start a new IME composition.
@@ -327,11 +327,13 @@ fn test_ime_composition(cx: &mut AppContext) {
 }
 
 #[gpui::test]
-fn test_selection_with_mouse(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_selection_with_mouse(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
 
-    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
-    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+    let (_, editor) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
+        build_editor(buffer, cx)
+    });
     editor.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
     });
@@ -392,10 +394,12 @@ fn test_selection_with_mouse(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_canceling_pending_selection(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_canceling_pending_selection(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
@@ -424,7 +428,7 @@ fn test_canceling_pending_selection(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_clone(cx: &mut gpui::AppContext) {
+fn test_clone(cx: &mut TestAppContext) {
     let (text, selection_ranges) = marked_text_ranges(
         indoc! {"
             one
@@ -435,10 +439,12 @@ fn test_clone(cx: &mut gpui::AppContext) {
         "},
         true,
     );
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(&text, cx);
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
 
-    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+    let (_, editor) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&text, cx);
+        build_editor(buffer, cx)
+    });
 
     editor.update(cx, |editor, cx| {
         editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
@@ -470,8 +476,8 @@ fn test_clone(cx: &mut gpui::AppContext) {
         snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
     );
     assert_set_eq!(
-        cloned_editor.read(cx).selections.ranges::<Point>(cx),
-        editor.read(cx).selections.ranges(cx)
+        cloned_editor.read_with(cx, |editor, cx| editor.selections.ranges::<Point>(cx)),
+        editor.read_with(cx, |editor, cx| editor.selections.ranges(cx))
     );
     assert_set_eq!(
         cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
@@ -480,19 +486,19 @@ fn test_clone(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_navigation_history(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_navigation_history(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     cx.set_global(DragAndDrop::<Workspace>::default());
     use workspace::item::Item;
-    let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(0, None, || &[], cx));
-    let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
+    let (_, pane) = cx.add_window(|cx| Pane::new(0, None, || &[], cx));
 
     cx.add_view(&pane, |cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
         let mut editor = build_editor(buffer.clone(), cx);
         let handle = cx.handle();
         editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
 
-        fn pop_history(editor: &mut Editor, cx: &mut AppContext) -> Option<NavigationEntry> {
+        fn pop_history(editor: &mut Editor, cx: &mut WindowContext) -> Option<NavigationEntry> {
             editor.nav_history.as_mut().unwrap().pop_backward(cx)
         }
 
@@ -590,10 +596,12 @@ fn test_navigation_history(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_cancel(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_cancel(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
@@ -630,30 +638,32 @@ fn test_cancel(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_fold_action(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(
-        &"
-            impl Foo {
-                // Hello!
+fn test_fold_action(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(
+            &"
+                impl Foo {
+                    // Hello!
 
-                fn a() {
-                    1
-                }
+                    fn a() {
+                        1
+                    }
 
-                fn b() {
-                    2
-                }
+                    fn b() {
+                        2
+                    }
 
-                fn c() {
-                    3
+                    fn c() {
+                        3
+                    }
                 }
-            }
-        "
-        .unindent(),
-        cx,
-    );
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+            "
+            .unindent(),
+            cx,
+        );
+        build_editor(buffer.clone(), cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -712,25 +722,26 @@ fn test_fold_action(cx: &mut gpui::AppContext) {
         );
 
         view.unfold_lines(&UnfoldLines, cx);
-        assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
+        assert_eq!(view.display_text(cx), view.buffer.read(cx).read(cx).text());
     });
 }
 
 #[gpui::test]
-fn test_move_cursor(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
-
-    buffer.update(cx, |buffer, cx| {
-        buffer.edit(
-            vec![
-                (Point::new(1, 0)..Point::new(1, 0), "\t"),
-                (Point::new(1, 1)..Point::new(1, 1), "\t"),
-            ],
-            None,
-            cx,
-        );
+fn test_move_cursor(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
+        buffer.update(cx, |buffer, cx| {
+            buffer.edit(
+                vec![
+                    (Point::new(1, 0)..Point::new(1, 0), "\t"),
+                    (Point::new(1, 1)..Point::new(1, 1), "\t"),
+                ],
+                None,
+                cx,
+            );
+        });
+        build_editor(buffer.clone(), cx)
     });
 
     view.update(cx, |view, cx| {
@@ -793,10 +804,12 @@ fn test_move_cursor(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_move_cursor_multibyte(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     assert_eq!('ⓐ'.len_utf8(), 3);
     assert_eq!('α'.len_utf8(), 2);
@@ -895,10 +908,12 @@ fn test_move_cursor_multibyte(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_move_cursor_different_line_lengths(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
+        build_editor(buffer.clone(), cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
@@ -942,10 +957,12 @@ fn test_move_cursor_different_line_lengths(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_beginning_end_of_line(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("abc\n  def", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_beginning_end_of_line(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\n  def", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -1102,10 +1119,12 @@ fn test_beginning_end_of_line(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_prev_next_word_boundary(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n  {baz.qux()}", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -1151,10 +1170,12 @@ fn test_prev_next_word_boundary(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("use one::{\n    two::three::four::five\n};", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.set_wrap_width(Some(140.), cx);
@@ -1330,10 +1351,12 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_delete_to_word_boundary(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("one two three four", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("one two three four", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -1345,10 +1368,9 @@ fn test_delete_to_word_boundary(cx: &mut gpui::AppContext) {
             ])
         });
         view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
+        assert_eq!(view.buffer.read(cx).read(cx).text(), "e two te four");
     });
 
-    assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
-
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -1359,16 +1381,17 @@ fn test_delete_to_word_boundary(cx: &mut gpui::AppContext) {
             ])
         });
         view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
+        assert_eq!(view.buffer.read(cx).read(cx).text(), "e t te our");
     });
-
-    assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
 }
 
 #[gpui::test]
-fn test_newline(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+fn test_newline(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaaa\n    bbbb\n", cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -1385,24 +1408,23 @@ fn test_newline(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_newline_with_old_selections(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(
-        "
-            a
-            b(
-                X
-            )
-            c(
-                X
-            )
-        "
-        .unindent()
-        .as_str(),
-        cx,
-    );
-
-    let (_, editor) = cx.add_window(Default::default(), |cx| {
+fn test_newline_with_old_selections(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, editor) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(
+            "
+                a
+                b(
+                    X
+                )
+                c(
+                    X
+                )
+            "
+            .unindent()
+            .as_str(),
+            cx,
+        );
         let mut editor = build_editor(buffer.clone(), cx);
         editor.change_selections(None, cx, |s| {
             s.select_ranges([
@@ -1413,28 +1435,27 @@ fn test_newline_with_old_selections(cx: &mut gpui::AppContext) {
         editor
     });
 
-    // Edit the buffer directly, deleting ranges surrounding the editor's selections
-    buffer.update(cx, |buffer, cx| {
-        buffer.edit(
-            [
-                (Point::new(1, 2)..Point::new(3, 0), ""),
-                (Point::new(4, 2)..Point::new(6, 0), ""),
-            ],
-            None,
-            cx,
-        );
-        assert_eq!(
-            buffer.read(cx).text(),
-            "
-                a
-                b()
-                c()
-            "
-            .unindent()
-        );
-    });
-
     editor.update(cx, |editor, cx| {
+        // Edit the buffer directly, deleting ranges surrounding the editor's selections
+        editor.buffer.update(cx, |buffer, cx| {
+            buffer.edit(
+                [
+                    (Point::new(1, 2)..Point::new(3, 0), ""),
+                    (Point::new(4, 2)..Point::new(6, 0), ""),
+                ],
+                None,
+                cx,
+            );
+            assert_eq!(
+                buffer.read(cx).text(),
+                "
+                    a
+                    b()
+                    c()
+                "
+                .unindent()
+            );
+        });
         assert_eq!(
             editor.selections.ranges(cx),
             &[
@@ -1517,22 +1538,21 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_insert_with_old_selections(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
-    let (_, editor) = cx.add_window(Default::default(), |cx| {
+fn test_insert_with_old_selections(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, editor) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
         let mut editor = build_editor(buffer.clone(), cx);
         editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
         editor
     });
 
-    // Edit the buffer directly, deleting ranges surrounding the editor's selections
-    buffer.update(cx, |buffer, cx| {
-        buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
-        assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
-    });
-
     editor.update(cx, |editor, cx| {
+        // Edit the buffer directly, deleting ranges surrounding the editor's selections
+        editor.buffer.update(cx, |buffer, cx| {
+            buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx);
+            assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
+        });
         assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
 
         editor.insert("Z", cx);
@@ -1836,24 +1856,26 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_indent_outdent_with_excerpts(cx: &mut gpui::AppContext) {
-    cx.set_global(
-        Settings::test(cx)
-            .with_language_defaults(
-                "TOML",
-                EditorSettings {
-                    tab_size: Some(2.try_into().unwrap()),
-                    ..Default::default()
-                },
-            )
-            .with_language_defaults(
-                "Rust",
-                EditorSettings {
-                    tab_size: Some(4.try_into().unwrap()),
-                    ..Default::default()
-                },
-            ),
-    );
+fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
+    cx.update(|cx| {
+        cx.set_global(
+            Settings::test(cx)
+                .with_language_defaults(
+                    "TOML",
+                    EditorSettings {
+                        tab_size: Some(2.try_into().unwrap()),
+                        ..Default::default()
+                    },
+                )
+                .with_language_defaults(
+                    "Rust",
+                    EditorSettings {
+                        tab_size: Some(4.try_into().unwrap()),
+                        ..Default::default()
+                    },
+                ),
+        );
+    });
     let toml_language = Arc::new(Language::new(
         LanguageConfig {
             name: "TOML".into(),
@@ -1895,7 +1917,7 @@ fn test_indent_outdent_with_excerpts(cx: &mut gpui::AppContext) {
         multibuffer
     });
 
-    cx.add_window(Default::default(), |cx| {
+    cx.add_window(|cx| {
         let mut editor = build_editor(multibuffer, cx);
 
         assert_eq!(
@@ -2022,10 +2044,12 @@ async fn test_delete(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_delete_line(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_delete_line(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2045,9 +2069,11 @@ fn test_delete_line(cx: &mut gpui::AppContext) {
         );
     });
 
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
@@ -2062,10 +2088,12 @@ fn test_delete_line(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_duplicate_line(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_duplicate_line(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2088,8 +2116,10 @@ fn test_duplicate_line(cx: &mut gpui::AppContext) {
         );
     });
 
-    let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2110,10 +2140,12 @@ fn test_duplicate_line(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_move_line_up_down(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_move_line_up_down(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.fold_ranges(
             vec![
@@ -2206,12 +2238,14 @@ fn test_move_line_up_down(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_move_line_up_down_with_blocks(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
-    let snapshot = buffer.read(cx).snapshot(cx);
-    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, editor) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     editor.update(cx, |editor, cx| {
+        let snapshot = editor.buffer.read(cx).snapshot(cx);
         editor.insert_blocks(
             [BlockProperties {
                 style: BlockStyle::Fixed,
@@ -2230,11 +2264,11 @@ fn test_move_line_up_down_with_blocks(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_transpose(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_transpose(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
 
     _ = cx
-        .add_window(Default::default(), |cx| {
+        .add_window(|cx| {
             let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
 
             editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
@@ -2255,7 +2289,7 @@ fn test_transpose(cx: &mut gpui::AppContext) {
         .1;
 
     _ = cx
-        .add_window(Default::default(), |cx| {
+        .add_window(|cx| {
             let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 
             editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
@@ -2281,7 +2315,7 @@ fn test_transpose(cx: &mut gpui::AppContext) {
         .1;
 
     _ = cx
-        .add_window(Default::default(), |cx| {
+        .add_window(|cx| {
             let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
 
             editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
@@ -2310,7 +2344,7 @@ fn test_transpose(cx: &mut gpui::AppContext) {
         .1;
 
     _ = cx
-        .add_window(Default::default(), |cx| {
+        .add_window(|cx| {
             let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
 
             editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
@@ -2524,10 +2558,12 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_select_all(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_select_all(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.select_all(&SelectAll, cx);
         assert_eq!(
@@ -2538,10 +2574,12 @@ fn test_select_all(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_select_line(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_select_line(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
             s.select_display_ranges([
@@ -2582,10 +2620,12 @@ fn test_select_line(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_split_selection_into_lines(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_split_selection_into_lines(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
+        build_editor(buffer, cx)
+    });
     view.update(cx, |view, cx| {
         view.fold_ranges(
             vec![
@@ -2650,10 +2690,12 @@ fn test_split_selection_into_lines(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_add_selection_above_below(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
-    let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
+fn test_add_selection_above_below(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, view) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
+        build_editor(buffer, cx)
+    });
 
     view.update(cx, |view, cx| {
         view.change_selections(None, cx, |s| {
@@ -4928,8 +4970,8 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_editing_disjoint_excerpts(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
     let multibuffer = cx.add_model(|cx| {
         let mut multibuffer = MultiBuffer::new(0);
@@ -4947,12 +4989,11 @@ fn test_editing_disjoint_excerpts(cx: &mut gpui::AppContext) {
             ],
             cx,
         );
+        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
         multibuffer
     });
 
-    assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
-
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
+    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
     view.update(cx, |view, cx| {
         assert_eq!(view.text(cx), "aaaa\nbbbb");
         view.change_selections(None, cx, |s| {
@@ -4975,8 +5016,8 @@ fn test_editing_disjoint_excerpts(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_editing_overlapping_excerpts(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let markers = vec![('[', ']').into(), ('(', ')').into()];
     let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
         indoc! {"
@@ -5000,7 +5041,7 @@ fn test_editing_overlapping_excerpts(cx: &mut gpui::AppContext) {
         multibuffer
     });
 
-    let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
+    let (_, view) = cx.add_window(|cx| build_editor(multibuffer, cx));
     view.update(cx, |view, cx| {
         let (expected_text, selection_ranges) = marked_text_ranges(
             indoc! {"
@@ -5048,8 +5089,8 @@ fn test_editing_overlapping_excerpts(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_refresh_selections(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_refresh_selections(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
     let mut excerpt1_id = None;
     let multibuffer = cx.add_model(|cx| {
@@ -5071,13 +5112,11 @@ fn test_refresh_selections(cx: &mut gpui::AppContext) {
             )
             .into_iter()
             .next();
+        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
         multibuffer
     });
-    assert_eq!(
-        multibuffer.read(cx).read(cx).text(),
-        "aaaa\nbbbb\nbbbb\ncccc"
-    );
-    let (_, editor) = cx.add_window(Default::default(), |cx| {
+
+    let (_, editor) = cx.add_window(|cx| {
         let mut editor = build_editor(multibuffer.clone(), cx);
         let snapshot = editor.snapshot(cx);
         editor.change_selections(None, cx, |s| {
@@ -5134,8 +5173,8 @@ fn test_refresh_selections(cx: &mut gpui::AppContext) {
 }
 
 #[gpui::test]
-fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::AppContext) {
-    cx.set_global(Settings::test(cx));
+fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
     let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
     let mut excerpt1_id = None;
     let multibuffer = cx.add_model(|cx| {
@@ -5157,13 +5196,11 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::AppContext)
             )
             .into_iter()
             .next();
+        assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
         multibuffer
     });
-    assert_eq!(
-        multibuffer.read(cx).read(cx).text(),
-        "aaaa\nbbbb\nbbbb\ncccc"
-    );
-    let (_, editor) = cx.add_window(Default::default(), |cx| {
+
+    let (_, editor) = cx.add_window(|cx| {
         let mut editor = build_editor(multibuffer.clone(), cx);
         let snapshot = editor.snapshot(cx);
         editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
@@ -5267,17 +5304,18 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
 }
 
 #[gpui::test]
-fn test_highlighted_ranges(cx: &mut gpui::AppContext) {
-    let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
-
-    cx.set_global(Settings::test(cx));
-    let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
+fn test_highlighted_ranges(cx: &mut TestAppContext) {
+    cx.update(|cx| cx.set_global(Settings::test(cx)));
+    let (_, editor) = cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
+        build_editor(buffer.clone(), cx)
+    });
 
     editor.update(cx, |editor, cx| {
         struct Type1;
         struct Type2;
 
-        let buffer = buffer.read(cx).snapshot(cx);
+        let buffer = editor.buffer.read(cx).snapshot(cx);
 
         let anchor_range =
             |range: Range<Point>| buffer.anchor_after(range.start)..buffer.anchor_after(range.end);
@@ -5761,7 +5799,6 @@ async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppCon
         &r#"
         ˇuse some::modified;
 
-
         fn main() {
             println!("hello there");
 
@@ -5783,7 +5820,6 @@ async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppCon
         &r#"
         use some::modified;
 
-
         fn main() {
         ˇ    println!("hello there");
 
@@ -5807,7 +5843,6 @@ async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppCon
         &r#"
         ˇuse some::modified;
 
-
         fn main() {
             println!("hello there");
 

crates/editor/src/element.rs 🔗

@@ -2493,24 +2493,24 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
 
 #[cfg(test)]
 mod tests {
-    use std::sync::Arc;
-
     use super::*;
     use crate::{
         display_map::{BlockDisposition, BlockProperties},
         Editor, MultiBuffer,
     };
+    use gpui::TestAppContext;
     use settings::Settings;
+    use std::sync::Arc;
     use util::test::sample_text;
 
     #[gpui::test]
-    fn test_layout_line_numbers(cx: &mut gpui::AppContext) {
-        cx.set_global(Settings::test(cx));
-        let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
-        let (_, editor) = cx.add_window(Default::default(), |cx| {
+    fn test_layout_line_numbers(cx: &mut TestAppContext) {
+        cx.update(|cx| cx.set_global(Settings::test(cx)));
+        let (_, editor) = cx.add_window(|cx| {
+            let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
             Editor::new(EditorMode::Full, buffer, None, None, cx)
         });
-        let element = EditorElement::new(editor.read(cx).style(cx));
+        let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
 
         let layouts = editor.update(cx, |editor, cx| {
             let snapshot = editor.snapshot(cx);
@@ -2522,10 +2522,10 @@ mod tests {
     }
 
     #[gpui::test]
-    fn test_layout_with_placeholder_text_and_blocks(cx: &mut gpui::AppContext) {
-        cx.set_global(Settings::test(cx));
-        let buffer = MultiBuffer::build_simple("", cx);
-        let (_, editor) = cx.add_window(Default::default(), |cx| {
+    fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
+        cx.update(|cx| cx.set_global(Settings::test(cx)));
+        let (_, editor) = cx.add_window(|cx| {
+            let buffer = MultiBuffer::build_simple("", cx);
             Editor::new(EditorMode::Full, buffer, None, None, cx)
         });
 
@@ -2546,7 +2546,7 @@ mod tests {
             cx.blur();
         });
 
-        let mut element = EditorElement::new(editor.read(cx).style(cx));
+        let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
         let (size, mut state) = editor.update(cx, |editor, cx| {
             element.layout(
                 SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),

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

@@ -34,11 +34,10 @@ impl<'a> EditorTestContext<'a> {
             crate::init(cx);
 
             let (window_id, editor) = cx.add_window(Default::default(), |cx| {
+                cx.focus_self();
                 build_editor(MultiBuffer::build_simple("", cx), cx)
             });
 
-            editor.update(cx, |_, cx| cx.focus_self());
-
             (window_id, editor)
         });
 
@@ -254,10 +253,10 @@ impl<'a> EditorTestContext<'a> {
             panic!(
                 indoc! {"
                     {}Editor has unexpected selections.
-                    
+
                     Expected selections:
                     {}
-                    
+
                     Actual selections:
                     {}
                 "},

crates/feedback/src/feedback_editor.rs 🔗

@@ -132,25 +132,18 @@ impl FeedbackEditor {
 
             if answer == Some(0) {
                 match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await {
-                    Ok(_) => {
-                        cx.update(|cx| {
-                            this.update(cx, |_, cx| {
-                                cx.dispatch_action(workspace::CloseActiveItem);
-                            })
-                        });
-                    }
+                    Ok(_) => this.update(&mut cx, |_, cx| {
+                        cx.dispatch_action(workspace::CloseActiveItem);
+                    }),
                     Err(error) => {
                         log::error!("{}", error);
-
-                        cx.update(|cx| {
-                            this.update(cx, |_, cx| {
-                                cx.prompt(
-                                    PromptLevel::Critical,
-                                    FEEDBACK_SUBMISSION_ERROR_TEXT,
-                                    &["OK"],
-                                );
-                            })
-                        });
+                        this.update(&mut cx, |_, cx| {
+                            cx.prompt(
+                                PromptLevel::Critical,
+                                FEEDBACK_SUBMISSION_ERROR_TEXT,
+                                &["OK"],
+                            );
+                        })
                     }
                 }
             }

crates/go_to_line/src/go_to_line.rs 🔗

@@ -142,12 +142,14 @@ impl Entity for GoToLine {
 
     fn release(&mut self, cx: &mut AppContext) {
         let scroll_position = self.prev_scroll_position.take();
-        self.active_editor.update(cx, |editor, cx| {
-            editor.highlight_rows(None);
-            if let Some(scroll_position) = scroll_position {
-                editor.set_scroll_position(scroll_position, cx);
-            }
-        })
+        cx.update_window(self.active_editor.window_id(), |cx| {
+            self.active_editor.update(cx, |editor, cx| {
+                editor.highlight_rows(None);
+                if let Some(scroll_position) = scroll_position {
+                    editor.set_scroll_position(scroll_position, cx);
+                }
+            })
+        });
     }
 }
 

crates/gpui/src/app.rs 🔗

@@ -937,7 +937,7 @@ impl AppContext {
         })
     }
 
-    pub fn subscribe_internal<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
+    fn subscribe_internal<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
     where
         E: Entity,
         E::Event: 'static,
@@ -1224,19 +1224,28 @@ impl AppContext {
         T: 'static,
         F: FnOnce(&mut T, &mut AppContext) -> U,
     {
-        self.update(|this| {
-            let type_id = TypeId::of::<T>();
-            if let Some(mut state) = this.globals.remove(&type_id) {
-                let result = update(state.downcast_mut().unwrap(), this);
-                this.globals.insert(type_id, state);
-                this.notify_global(type_id);
-                result
-            } else {
-                panic!("No global added for {}", std::any::type_name::<T>());
-            }
+        self.update(|mut this| {
+            Self::update_global_internal(&mut this, |global, cx| update(global, cx))
         })
     }
 
+    fn update_global_internal<C, T, F, U>(this: &mut C, update: F) -> U
+    where
+        C: DerefMut<Target = AppContext>,
+        T: 'static,
+        F: FnOnce(&mut T, &mut C) -> U,
+    {
+        let type_id = TypeId::of::<T>();
+        if let Some(mut state) = this.globals.remove(&type_id) {
+            let result = update(state.downcast_mut().unwrap(), this);
+            this.globals.insert(type_id, state);
+            this.notify_global(type_id);
+            result
+        } else {
+            panic!("No global added for {}", std::any::type_name::<T>());
+        }
+    }
+
     pub fn clear_globals(&mut self) {
         self.globals.clear();
     }
@@ -2714,15 +2723,6 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         self.app.add_model(build_model)
     }
 
-    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ModelContext<T>)) {
-        let handle = self.handle();
-        self.app.defer(move |cx| {
-            handle.update(cx, |model, cx| {
-                callback(model, cx);
-            })
-        })
-    }
-
     pub fn emit(&mut self, payload: T::Event) {
         self.app.pending_effects.push_back(Effect::Event {
             entity_id: self.model_id,
@@ -3090,21 +3090,17 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
         H: Handle<E>,
         F: 'static + FnMut(&mut V, H, &E::Event, &mut ViewContext<V>),
     {
-        let window_id = self.window_id;
         let subscriber = self.weak_handle();
         self.window_context
             .subscribe_internal(handle, move |emitter, event, cx| {
-                cx.update_window(window_id, |cx| {
-                    if let Some(subscriber) = subscriber.upgrade(cx) {
-                        subscriber.update(cx, |subscriber, cx| {
-                            callback(subscriber, emitter, event, cx);
-                        });
-                        true
-                    } else {
-                        false
-                    }
-                })
-                .unwrap_or(false)
+                if let Some(subscriber) = subscriber.upgrade(cx) {
+                    subscriber.update(cx, |subscriber, cx| {
+                        callback(subscriber, emitter, event, cx);
+                    });
+                    true
+                } else {
+                    false
+                }
             })
     }
 
@@ -3333,15 +3329,9 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
     }
 
     pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>)) {
-        let window_id = self.window_id;
         let handle = self.handle();
-        self.window_context.defer(move |cx| {
-            cx.update_window(window_id, |cx| {
-                handle.update(cx, |view, cx| {
-                    callback(view, cx);
-                })
-            });
-        })
+        self.window_context
+            .defer(move |cx| handle.update(cx, |view, cx| callback(view, cx)))
     }
 
     pub fn after_window_update(
@@ -4651,7 +4641,7 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_defer_and_after_window_update(cx: &mut AppContext) {
+    fn test_defer_and_after_window_update(cx: &mut TestAppContext) {
         struct View {
             render_count: usize,
         }
@@ -4671,7 +4661,7 @@ mod tests {
             }
         }
 
-        let (_, view) = cx.add_window(Default::default(), |_| View { render_count: 0 });
+        let (_, view) = cx.add_window(|_| View { render_count: 0 });
         let called_defer = Rc::new(AtomicBool::new(false));
         let called_after_window_update = Rc::new(AtomicBool::new(false));
 
@@ -4699,11 +4689,11 @@ mod tests {
 
         assert!(called_defer.load(SeqCst));
         assert!(called_after_window_update.load(SeqCst));
-        assert_eq!(view.read(cx).render_count, 3);
+        assert_eq!(view.read_with(cx, |view, _| view.render_count), 3);
     }
 
     #[crate::test(self)]
-    fn test_view_handles(cx: &mut AppContext) {
+    fn test_view_handles(cx: &mut TestAppContext) {
         struct View {
             other: Option<ViewHandle<View>>,
             events: Vec<String>,
@@ -4738,33 +4728,39 @@ mod tests {
             }
         }
 
-        let (_, root_view) = cx.add_window(Default::default(), |cx| View::new(None, cx));
+        let (_, root_view) = cx.add_window(|cx| View::new(None, cx));
         let handle_1 = cx.add_view(&root_view, |cx| View::new(None, cx));
         let handle_2 = cx.add_view(&root_view, |cx| View::new(Some(handle_1.clone()), cx));
-        assert_eq!(cx.views.len(), 3);
+        assert_eq!(cx.read(|cx| cx.views.len()), 3);
 
         handle_1.update(cx, |view, cx| {
             view.events.push("updated".into());
             cx.emit(1);
             cx.emit(2);
         });
-        assert_eq!(handle_1.read(cx).events, vec!["updated".to_string()]);
-        assert_eq!(
-            handle_2.read(cx).events,
-            vec![
-                "observed event 1".to_string(),
-                "observed event 2".to_string(),
-            ]
-        );
+        handle_1.read_with(cx, |view, _| {
+            assert_eq!(view.events, vec!["updated".to_string()]);
+        });
+        handle_2.read_with(cx, |view, _| {
+            assert_eq!(
+                view.events,
+                vec![
+                    "observed event 1".to_string(),
+                    "observed event 2".to_string(),
+                ]
+            );
+        });
 
         handle_2.update(cx, |view, _| {
             drop(handle_1);
             view.other.take();
         });
 
-        assert_eq!(cx.views.len(), 2);
-        assert!(cx.subscriptions.is_empty());
-        assert!(cx.observations.is_empty());
+        cx.read(|cx| {
+            assert_eq!(cx.views.len(), 2);
+            assert!(cx.subscriptions.is_empty());
+            assert!(cx.observations.is_empty());
+        });
     }
 
     #[crate::test(self)]
@@ -4887,14 +4883,14 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_view_events(cx: &mut AppContext) {
+    fn test_view_events(cx: &mut TestAppContext) {
         struct Model;
 
         impl Entity for Model {
             type Event = String;
         }
 
-        let (_, handle_1) = cx.add_window(Default::default(), |_| TestView::default());
+        let (_, handle_1) = cx.add_window(|_| TestView::default());
         let handle_2 = cx.add_view(&handle_1, |_| TestView::default());
         let handle_3 = cx.add_model(|_| Model);
 
@@ -4916,16 +4912,17 @@ mod tests {
         });
 
         handle_2.update(cx, |_, c| c.emit("7".into()));
-        assert_eq!(handle_1.read(cx).events, vec!["7"]);
+        handle_1.read_with(cx, |view, _| assert_eq!(view.events, ["7"]));
 
         handle_2.update(cx, |_, c| c.emit("5".into()));
-        assert_eq!(handle_1.read(cx).events, vec!["7", "5", "5 from inner"]);
+        handle_1.read_with(cx, |view, _| {
+            assert_eq!(view.events, ["7", "5", "5 from inner"])
+        });
 
         handle_3.update(cx, |_, c| c.emit("9".into()));
-        assert_eq!(
-            handle_1.read(cx).events,
-            vec!["7", "5", "5 from inner", "9"]
-        );
+        handle_1.read_with(cx, |view, _| {
+            assert_eq!(view.events, ["7", "5", "5 from inner", "9"])
+        });
     }
 
     #[crate::test(self)]
@@ -5113,14 +5110,14 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_dropping_subscribers(cx: &mut AppContext) {
+    fn test_dropping_subscribers(cx: &mut TestAppContext) {
         struct Model;
 
         impl Entity for Model {
             type Event = ();
         }
 
-        let (_, root_view) = cx.add_window(Default::default(), |_| TestView::default());
+        let (_, root_view) = cx.add_window(|_| TestView::default());
         let observing_view = cx.add_view(&root_view, |_| TestView::default());
         let emitting_view = cx.add_view(&root_view, |_| TestView::default());
         let observing_model = cx.add_model(|_| Model);
@@ -5165,7 +5162,7 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_observe_and_notify_from_view(cx: &mut AppContext) {
+    fn test_observe_and_notify_from_view(cx: &mut TestAppContext) {
         #[derive(Default)]
         struct Model {
             state: String,
@@ -5175,7 +5172,7 @@ mod tests {
             type Event = ();
         }
 
-        let (_, view) = cx.add_window(Default::default(), |_| TestView::default());
+        let (_, view) = cx.add_window(|_| TestView::default());
         let model = cx.add_model(|_| Model {
             state: "old-state".into(),
         });
@@ -5191,7 +5188,7 @@ mod tests {
             model.state = "new-state".into();
             cx.notify();
         });
-        assert_eq!(view.read(cx).events, vec!["new-state"]);
+        view.read_with(cx, |view, _| assert_eq!(view.events, ["new-state"]));
     }
 
     #[crate::test(self)]
@@ -5216,14 +5213,14 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_notify_and_drop_observe_subscription_in_same_update_cycle(cx: &mut AppContext) {
+    fn test_notify_and_drop_observe_subscription_in_same_update_cycle(cx: &mut TestAppContext) {
         struct Model;
         impl Entity for Model {
             type Event = ();
         }
 
         let model = cx.add_model(|_| Model);
-        let (_, view) = cx.add_window(Default::default(), |_| TestView::default());
+        let (_, view) = cx.add_window(|_| TestView::default());
 
         view.update(cx, |_, cx| {
             model.update(cx, |_, cx| cx.notify());
@@ -5236,19 +5233,18 @@ mod tests {
         for _ in 0..3 {
             model.update(cx, |_, cx| cx.notify());
         }
-
-        assert_eq!(view.read(cx).events, Vec::<String>::new());
+        view.read_with(cx, |view, _| assert_eq!(view.events, Vec::<&str>::new()));
     }
 
     #[crate::test(self)]
-    fn test_dropping_observers(cx: &mut AppContext) {
+    fn test_dropping_observers(cx: &mut TestAppContext) {
         struct Model;
 
         impl Entity for Model {
             type Event = ();
         }
 
-        let (_, root_view) = cx.add_window(Default::default(), |_| TestView::default());
+        let (_, root_view) = cx.add_window(|_| TestView::default());
         let observing_view = cx.add_view(&root_view, |_| TestView::default());
         let observing_model = cx.add_model(|_| Model);
         let observed_model = cx.add_model(|_| Model);
@@ -5269,7 +5265,7 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_dropping_subscriptions_during_callback(cx: &mut AppContext) {
+    fn test_dropping_subscriptions_during_callback(cx: &mut TestAppContext) {
         struct Model;
 
         impl Entity for Model {
@@ -5371,7 +5367,7 @@ mod tests {
             }
         }
 
-        let (_, root_view) = cx.add_window(Default::default(), |_| View);
+        let (_, root_view) = cx.add_window(|_| View);
         let observing_view = cx.add_view(&root_view, |_| View);
         let observed_view = cx.add_view(&root_view, |_| View);
 
@@ -5410,13 +5406,15 @@ mod tests {
             }
         }));
 
-        cx.default_global::<()>();
-        cx.set_global(());
+        cx.update(|cx| {
+            cx.default_global::<()>();
+            cx.set_global(());
+        });
         assert_eq!(*observation_count.borrow(), 1);
     }
 
     #[crate::test(self)]
-    fn test_focus(cx: &mut AppContext) {
+    fn test_focus(cx: &mut TestAppContext) {
         struct View {
             name: String,
             events: Arc<Mutex<Vec<String>>>,
@@ -5449,7 +5447,7 @@ mod tests {
         }
 
         let view_events: Arc<Mutex<Vec<String>>> = Default::default();
-        let (window_id, view_1) = cx.add_window(Default::default(), |_| View {
+        let (window_id, view_1) = cx.add_window(|_| View {
             events: view_events.clone(),
             name: "view 1".to_string(),
         });
@@ -6295,7 +6293,7 @@ mod tests {
     }
 
     #[crate::test(self)]
-    fn test_child_view(cx: &mut AppContext) {
+    fn test_child_view(cx: &mut TestAppContext) {
         struct Child {
             rendered: Rc<Cell<bool>>,
             dropped: Rc<Cell<bool>>,
@@ -6346,7 +6344,7 @@ mod tests {
 
         let child_rendered = Rc::new(Cell::new(false));
         let child_dropped = Rc::new(Cell::new(false));
-        let (_, root_view) = cx.add_window(Default::default(), |cx| Parent {
+        let (_, root_view) = cx.add_window(|cx| Parent {
             child: Some(cx.add_view(|_| Child {
                 rendered: child_rendered.clone(),
                 dropped: child_dropped.clone(),

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

@@ -1,4 +1,5 @@
 use std::{
+    any::Any,
     cell::RefCell,
     mem,
     path::PathBuf,
@@ -22,8 +23,8 @@ use crate::{
     platform,
     platform::{Event, InputHandler, KeyDownEvent, Platform},
     Action, AnyViewHandle, AppContext, Entity, FontCache, Handle, ModelContext, ModelHandle,
-    ReadModelWith, ReadViewWith, Task, UpdateModel, UpdateView, View, ViewContext, ViewHandle,
-    WeakHandle, WindowContext,
+    ReadModelWith, ReadViewWith, Subscription, Task, UpdateModel, UpdateView, View, ViewContext,
+    ViewHandle, WeakHandle, WindowContext,
 };
 use collections::BTreeMap;
 
@@ -160,6 +161,26 @@ impl TestAppContext {
         self.cx.borrow_mut().add_view(parent_handle, build_view)
     }
 
+    pub fn observe_global<E, F>(&mut self, callback: F) -> Subscription
+    where
+        E: Any,
+        F: 'static + FnMut(&mut AppContext),
+    {
+        self.cx.borrow_mut().observe_global::<E, F>(callback)
+    }
+
+    pub fn set_global<T: 'static>(&mut self, state: T) {
+        self.cx.borrow_mut().set_global(state);
+    }
+
+    pub fn subscribe_global<E, F>(&mut self, callback: F) -> Subscription
+    where
+        E: Any,
+        F: 'static + FnMut(&E, &mut AppContext),
+    {
+        self.cx.borrow_mut().subscribe_global(callback)
+    }
+
     pub fn window_ids(&self) -> Vec<usize> {
         self.cx.borrow().window_ids().collect()
     }

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

@@ -13,10 +13,11 @@ use crate::{
     },
     text_layout::TextLayoutCache,
     util::post_inc,
-    Action, AnyView, AnyViewHandle, AnyWeakViewHandle, AppContext, Drawable, Effect, Entity,
-    ModelContext, ModelHandle, MouseRegion, MouseRegionId, ParentId, ReadModel, ReadView,
-    SceneBuilder, Subscription, UpdateModel, UpdateView, UpgradeViewHandle, View, ViewContext,
-    ViewHandle, WeakViewHandle, WindowInvalidation,
+    Action, AnyModelHandle, AnyView, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle,
+    AppContext, Drawable, Effect, Entity, Handle, ModelContext, ModelHandle, MouseRegion,
+    MouseRegionId, ParentId, ReadModel, ReadView, SceneBuilder, Subscription, UpdateModel,
+    UpdateView, UpgradeModelHandle, UpgradeViewHandle, View, ViewContext, ViewHandle,
+    WeakModelHandle, WeakViewHandle, WindowInvalidation,
 };
 use anyhow::{anyhow, bail, Result};
 use collections::{HashMap, HashSet};
@@ -175,6 +176,23 @@ impl UpdateView for WindowContext<'_, '_> {
     }
 }
 
+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)
+    }
+
+    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)
@@ -239,6 +257,49 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
         Some(result)
     }
 
+    pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut WindowContext)) {
+        let window_id = self.window_id;
+        self.app_context.defer(move |cx| {
+            cx.update_window(window_id, |cx| callback(cx));
+        })
+    }
+
+    pub fn update_global<T, F, U>(&mut self, update: F) -> U
+    where
+        T: 'static,
+        F: FnOnce(&mut T, &mut Self) -> U,
+    {
+        AppContext::update_global_internal(self, |global, cx| update(global, cx))
+    }
+
+    pub fn subscribe<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
+    where
+        E: Entity,
+        E::Event: 'static,
+        H: Handle<E>,
+        F: 'static + FnMut(H, &E::Event, &mut WindowContext),
+    {
+        self.subscribe_internal(handle, move |emitter, event, cx| {
+            callback(emitter, event, cx);
+            true
+        })
+    }
+
+    pub fn subscribe_internal<E, H, F>(&mut self, handle: &H, mut callback: F) -> Subscription
+    where
+        E: Entity,
+        E::Event: 'static,
+        H: Handle<E>,
+        F: 'static + FnMut(H, &E::Event, &mut WindowContext) -> bool,
+    {
+        let window_id = self.window_id;
+        self.app_context
+            .subscribe_internal(handle, move |emitter, event, cx| {
+                cx.update_window(window_id, |cx| callback(emitter, event, cx))
+                    .unwrap_or(false)
+            })
+    }
+
     pub(crate) fn observe_window_activation<F>(&mut self, callback: F) -> Subscription
     where
         F: 'static + FnMut(bool, &mut WindowContext) -> bool,

crates/gpui_macros/src/gpui_macros.rs 🔗

@@ -174,23 +174,60 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
             }
         }
     } else {
+        // Pass to the test function the number of app contexts that it needs,
+        // based on its parameter list.
+        let mut cx_vars = proc_macro2::TokenStream::new();
+        let mut cx_teardowns = proc_macro2::TokenStream::new();
         let mut inner_fn_args = proc_macro2::TokenStream::new();
-        for arg in inner_fn.sig.inputs.iter() {
+        for (ix, arg) in inner_fn.sig.inputs.iter().enumerate() {
             if let FnArg::Typed(arg) = arg {
                 if let Type::Path(ty) = &*arg.ty {
                     let last_segment = ty.path.segments.last();
 
                     if let Some("StdRng") = last_segment.map(|s| s.ident.to_string()).as_deref() {
                         inner_fn_args.extend(quote!(rand::SeedableRng::seed_from_u64(seed),));
+                        continue;
+                    }
+                } else if let Type::Reference(ty) = &*arg.ty {
+                    if let Type::Path(ty) = &*ty.elem {
+                        let last_segment = ty.path.segments.last();
+                        match last_segment.map(|s| s.ident.to_string()).as_deref() {
+                            Some("AppContext") => {
+                                inner_fn_args.extend(quote!(cx,));
+                                continue;
+                            }
+                            Some("TestAppContext") => {
+                                let first_entity_id = ix * 100_000;
+                                let cx_varname = format_ident!("cx_{}", ix);
+                                cx_vars.extend(quote!(
+                                    let mut #cx_varname = #namespace::TestAppContext::new(
+                                        foreground_platform.clone(),
+                                        cx.platform().clone(),
+                                        deterministic.build_foreground(#ix),
+                                        deterministic.build_background(),
+                                        cx.font_cache().clone(),
+                                        cx.leak_detector(),
+                                        #first_entity_id,
+                                        stringify!(#outer_fn_name).to_string(),
+                                    );
+                                ));
+                                cx_teardowns.extend(quote!(
+                                    #cx_varname.update(|cx| cx.remove_all_windows());
+                                    deterministic.run_until_parked();
+                                    #cx_varname.update(|cx| cx.clear_globals());
+                                ));
+                                inner_fn_args.extend(quote!(&mut #cx_varname,));
+                                continue;
+                            }
+                            _ => {}
+                        }
                     }
-                } else {
-                    inner_fn_args.extend(quote!(cx,));
                 }
-            } else {
-                return TokenStream::from(
-                    syn::Error::new_spanned(arg, "invalid argument").into_compile_error(),
-                );
             }
+
+            return TokenStream::from(
+                syn::Error::new_spanned(arg, "invalid argument").into_compile_error(),
+            );
         }
 
         parse_quote! {
@@ -203,7 +240,11 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                     #starting_seed as u64,
                     #max_retries,
                     #detect_nondeterminism,
-                    &mut |cx, _, _, seed| #inner_fn_name(#inner_fn_args),
+                    &mut |cx, foreground_platform, deterministic, seed| {
+                        #cx_vars
+                        #inner_fn_name(#inner_fn_args);
+                        #cx_teardowns
+                    },
                     #on_failure_fn_name,
                     stringify!(#outer_fn_name).to_string(),
                 );

crates/outline/src/outline.rs 🔗

@@ -5,7 +5,7 @@ use editor::{
 use fuzzy::StringMatch;
 use gpui::{
     actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Entity,
-    MouseState, Task, View, ViewContext, ViewHandle,
+    MouseState, Task, View, ViewContext, ViewHandle, WindowContext,
 };
 use language::Outline;
 use ordered_float::OrderedFloat;
@@ -39,7 +39,9 @@ impl Entity for OutlineView {
     type Event = Event;
 
     fn release(&mut self, cx: &mut AppContext) {
-        self.restore_active_editor(cx);
+        cx.update_window(self.active_editor.window_id(), |cx| {
+            self.restore_active_editor(cx);
+        });
     }
 }
 
@@ -100,7 +102,7 @@ impl OutlineView {
         }
     }
 
-    fn restore_active_editor(&mut self, cx: &mut AppContext) {
+    fn restore_active_editor(&mut self, cx: &mut WindowContext) {
         self.active_editor.update(cx, |editor, cx| {
             editor.highlight_rows(None);
             if let Some(scroll_position) = self.prev_scroll_position {

crates/vim/src/editor_events.rs 🔗

@@ -11,13 +11,9 @@ pub fn init(cx: &mut AppContext) {
 
 fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) {
     Vim::update(cx, |vim, cx| {
-        if let Some(previously_active_editor) = vim
-            .active_editor
-            .as_ref()
-            .and_then(|editor| editor.upgrade(cx))
-        {
-            vim.unhook_vim_settings(previously_active_editor, cx);
-        }
+        vim.update_active_editor(cx, |previously_active_editor, cx| {
+            Vim::unhook_vim_settings(previously_active_editor, cx);
+        });
 
         vim.active_editor = Some(editor.downgrade());
         vim.editor_subscription = Some(cx.subscribe(editor, |editor, event, cx| match event {
@@ -55,7 +51,10 @@ fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) {
                 vim.active_editor = None;
             }
         }
-        vim.unhook_vim_settings(editor.clone(), cx);
+
+        cx.update_window(editor.window_id(), |cx| {
+            editor.update(cx, |editor, cx| Vim::unhook_vim_settings(editor, cx))
+        });
     })
 }
 

crates/vim/src/vim.rs 🔗

@@ -15,8 +15,7 @@ use std::sync::Arc;
 use collections::CommandPaletteFilter;
 use editor::{Bias, Cancel, Editor, EditorMode};
 use gpui::{
-    actions, impl_actions, AppContext, Subscription, ViewContext, ViewHandle, WeakViewHandle,
-    WindowContext,
+    actions, impl_actions, AppContext, Subscription, ViewContext, WeakViewHandle, WindowContext,
 };
 use language::CursorShape;
 use motion::Motion;
@@ -148,10 +147,8 @@ impl Vim {
         cx: &mut AppContext,
         update: impl FnOnce(&mut Editor, &mut ViewContext<Editor>) -> S,
     ) -> Option<S> {
-        self.active_editor
-            .clone()
-            .and_then(|ae| ae.upgrade(cx))
-            .map(|ae| ae.update(cx, update))
+        let editor = self.active_editor.clone()?.upgrade(cx)?;
+        cx.update_window(editor.window_id(), |cx| editor.update(cx, update))
     }
 
     fn switch_mode(&mut self, mode: Mode, leave_selections: bool, cx: &mut AppContext) {
@@ -166,27 +163,19 @@ impl Vim {
         }
 
         // Adjust selections
-        if let Some(editor) = self
-            .active_editor
-            .as_ref()
-            .and_then(|editor| editor.upgrade(cx))
-        {
-            editor.update(cx, |editor, cx| {
-                editor.change_selections(None, cx, |s| {
-                    s.move_with(|map, selection| {
-                        if self.state.empty_selections_only() {
-                            let new_head = map.clip_point(selection.head(), Bias::Left);
-                            selection.collapse_to(new_head, selection.goal)
-                        } else {
-                            selection.set_head(
-                                map.clip_point(selection.head(), Bias::Left),
-                                selection.goal,
-                            );
-                        }
-                    });
-                })
+        self.update_active_editor(cx, |editor, cx| {
+            editor.change_selections(None, cx, |s| {
+                s.move_with(|map, selection| {
+                    if self.state.empty_selections_only() {
+                        let new_head = map.clip_point(selection.head(), Bias::Left);
+                        selection.collapse_to(new_head, selection.goal)
+                    } else {
+                        selection
+                            .set_head(map.clip_point(selection.head(), Bias::Left), selection.goal);
+                    }
+                });
             })
-        }
+        });
     }
 
     fn push_operator(&mut self, operator: Operator, cx: &mut AppContext) {
@@ -272,33 +261,25 @@ impl Vim {
             }
         });
 
-        if let Some(editor) = self
-            .active_editor
-            .as_ref()
-            .and_then(|editor| editor.upgrade(cx))
-        {
-            if self.enabled && editor.read(cx).mode() == EditorMode::Full {
-                editor.update(cx, |editor, cx| {
-                    editor.set_cursor_shape(cursor_shape, cx);
-                    editor.set_clip_at_line_ends(state.clip_at_line_end(), cx);
-                    editor.set_input_enabled(!state.vim_controlled());
-                    editor.selections.line_mode = matches!(state.mode, Mode::Visual { line: true });
-                    let context_layer = state.keymap_context_layer();
-                    editor.set_keymap_context_layer::<Self>(context_layer);
-                });
+        self.update_active_editor(cx, |editor, cx| {
+            if self.enabled && editor.mode() == EditorMode::Full {
+                editor.set_cursor_shape(cursor_shape, cx);
+                editor.set_clip_at_line_ends(state.clip_at_line_end(), cx);
+                editor.set_input_enabled(!state.vim_controlled());
+                editor.selections.line_mode = matches!(state.mode, Mode::Visual { line: true });
+                let context_layer = state.keymap_context_layer();
+                editor.set_keymap_context_layer::<Self>(context_layer);
             } else {
-                self.unhook_vim_settings(editor, cx);
+                Self::unhook_vim_settings(editor, cx);
             }
-        }
+        });
     }
 
-    fn unhook_vim_settings(&self, editor: ViewHandle<Editor>, cx: &mut AppContext) {
-        editor.update(cx, |editor, cx| {
-            editor.set_cursor_shape(CursorShape::Bar, cx);
-            editor.set_clip_at_line_ends(false, cx);
-            editor.set_input_enabled(true);
-            editor.selections.line_mode = false;
-            editor.remove_keymap_context_layer::<Self>();
-        });
+    fn unhook_vim_settings(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+        editor.set_cursor_shape(CursorShape::Bar, cx);
+        editor.set_clip_at_line_ends(false, cx);
+        editor.set_input_enabled(true);
+        editor.selections.line_mode = false;
+        editor.remove_keymap_context_layer::<Self>();
     }
 }

crates/workspace/src/item.rs 🔗

@@ -171,7 +171,7 @@ pub trait ItemHandle: 'static + fmt::Debug {
     fn subscribe_to_item_events(
         &self,
         cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut AppContext)>,
+        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
     ) -> gpui::Subscription;
     fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
     fn tab_content(
@@ -254,7 +254,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
     fn subscribe_to_item_events(
         &self,
         cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut AppContext)>,
+        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
     ) -> gpui::Subscription {
         cx.subscribe(self, move |_, event, cx| {
             for item_event in T::to_item_events(event) {
@@ -677,7 +677,7 @@ pub trait FollowableItem: Item {
 
 pub trait FollowableItemHandle: ItemHandle {
     fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId>;
-    fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut AppContext);
+    fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut WindowContext);
     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
     fn add_event_to_update_proto(
         &self,
@@ -689,7 +689,7 @@ pub trait FollowableItemHandle: ItemHandle {
         &self,
         project: &ModelHandle<Project>,
         message: proto::update_view::Variant,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>>;
     fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool;
 }
@@ -704,7 +704,7 @@ impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
         })
     }
 
-    fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut AppContext) {
+    fn set_leader_replica_id(&self, leader_replica_id: Option<u16>, cx: &mut WindowContext) {
         self.update(cx, |this, cx| {
             this.set_leader_replica_id(leader_replica_id, cx)
         })
@@ -731,7 +731,7 @@ impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
         &self,
         project: &ModelHandle<Project>,
         message: proto::update_view::Variant,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Result<()>> {
         self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
     }

crates/workspace/src/pane.rs 🔗

@@ -1890,15 +1890,15 @@ fn render_tab_bar_button<A: Action + Clone>(
 }
 
 impl ItemNavHistory {
-    pub fn push<D: 'static + Any>(&self, data: Option<D>, cx: &mut AppContext) {
+    pub fn push<D: 'static + Any>(&self, data: Option<D>, cx: &mut WindowContext) {
         self.history.borrow_mut().push(data, self.item.clone(), cx);
     }
 
-    pub fn pop_backward(&self, cx: &mut AppContext) -> Option<NavigationEntry> {
+    pub fn pop_backward(&self, cx: &mut WindowContext) -> Option<NavigationEntry> {
         self.history.borrow_mut().pop(NavigationMode::GoingBack, cx)
     }
 
-    pub fn pop_forward(&self, cx: &mut AppContext) -> Option<NavigationEntry> {
+    pub fn pop_forward(&self, cx: &mut WindowContext) -> Option<NavigationEntry> {
         self.history
             .borrow_mut()
             .pop(NavigationMode::GoingForward, cx)
@@ -1918,7 +1918,7 @@ impl NavHistory {
         self.mode = NavigationMode::Normal;
     }
 
-    fn pop(&mut self, mode: NavigationMode, cx: &mut AppContext) -> Option<NavigationEntry> {
+    fn pop(&mut self, mode: NavigationMode, cx: &mut WindowContext) -> Option<NavigationEntry> {
         let entry = match mode {
             NavigationMode::Normal | NavigationMode::Disabled | NavigationMode::ClosingItem => {
                 return None
@@ -1938,7 +1938,7 @@ impl NavHistory {
         &mut self,
         data: Option<D>,
         item: Rc<dyn WeakItemHandle>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) {
         match self.mode {
             NavigationMode::Disabled => {}
@@ -1983,7 +1983,7 @@ impl NavHistory {
         self.did_update(cx);
     }
 
-    fn did_update(&self, cx: &mut AppContext) {
+    fn did_update(&self, cx: &mut WindowContext) {
         if let Some(pane) = self.pane.upgrade(cx) {
             cx.defer(move |cx| pane.update(cx, |pane, cx| pane.history_updated(cx)));
         }

crates/workspace/src/searchable.rs 🔗

@@ -2,7 +2,7 @@ use std::any::Any;
 
 use gpui::{
     AnyViewHandle, AnyWeakViewHandle, AppContext, Subscription, Task, ViewContext, ViewHandle,
-    WeakViewHandle,
+    WeakViewHandle, WindowContext,
 };
 use project::search::SearchQuery;
 
@@ -90,29 +90,34 @@ pub trait SearchableItemHandle: ItemHandle {
     fn supported_options(&self) -> SearchOptions;
     fn subscribe_to_search_events(
         &self,
-        cx: &mut AppContext,
-        handler: Box<dyn Fn(SearchEvent, &mut AppContext)>,
+        cx: &mut WindowContext,
+        handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
     ) -> Subscription;
-    fn clear_matches(&self, cx: &mut AppContext);
-    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut AppContext);
-    fn query_suggestion(&self, cx: &mut AppContext) -> String;
-    fn activate_match(&self, index: usize, matches: &Vec<Box<dyn Any + Send>>, cx: &mut AppContext);
+    fn clear_matches(&self, cx: &mut WindowContext);
+    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
+    fn query_suggestion(&self, cx: &mut WindowContext) -> String;
+    fn activate_match(
+        &self,
+        index: usize,
+        matches: &Vec<Box<dyn Any + Send>>,
+        cx: &mut WindowContext,
+    );
     fn match_index_for_direction(
         &self,
         matches: &Vec<Box<dyn Any + Send>>,
         current_index: usize,
         direction: Direction,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> usize;
     fn find_matches(
         &self,
         query: SearchQuery,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Vec<Box<dyn Any + Send>>>;
     fn active_match_index(
         &self,
         matches: &Vec<Box<dyn Any + Send>>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Option<usize>;
 }
 
@@ -131,8 +136,8 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
 
     fn subscribe_to_search_events(
         &self,
-        cx: &mut AppContext,
-        handler: Box<dyn Fn(SearchEvent, &mut AppContext)>,
+        cx: &mut WindowContext,
+        handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
     ) -> Subscription {
         cx.subscribe(self, move |_, event, cx| {
             if let Some(search_event) = T::to_search_event(event) {
@@ -141,21 +146,21 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
         })
     }
 
-    fn clear_matches(&self, cx: &mut AppContext) {
+    fn clear_matches(&self, cx: &mut WindowContext) {
         self.update(cx, |this, cx| this.clear_matches(cx));
     }
-    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut AppContext) {
+    fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext) {
         let matches = downcast_matches(matches);
         self.update(cx, |this, cx| this.update_matches(matches, cx));
     }
-    fn query_suggestion(&self, cx: &mut AppContext) -> String {
+    fn query_suggestion(&self, cx: &mut WindowContext) -> String {
         self.update(cx, |this, cx| this.query_suggestion(cx))
     }
     fn activate_match(
         &self,
         index: usize,
         matches: &Vec<Box<dyn Any + Send>>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) {
         let matches = downcast_matches(matches);
         self.update(cx, |this, cx| this.activate_match(index, matches, cx));
@@ -165,7 +170,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
         matches: &Vec<Box<dyn Any + Send>>,
         current_index: usize,
         direction: Direction,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> usize {
         let matches = downcast_matches(matches);
         self.update(cx, |this, cx| {
@@ -175,7 +180,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
     fn find_matches(
         &self,
         query: SearchQuery,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Task<Vec<Box<dyn Any + Send>>> {
         let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
         cx.foreground().spawn(async {
@@ -189,7 +194,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
     fn active_match_index(
         &self,
         matches: &Vec<Box<dyn Any + Send>>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> Option<usize> {
         let matches = downcast_matches(matches);
         self.update(cx, |this, cx| this.active_match_index(matches, cx))

crates/workspace/src/toolbar.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{ItemHandle, Pane};
 use gpui::{
     elements::*, platform::CursorStyle, platform::MouseButton, Action, AnyViewHandle, AppContext,
-    Element, Entity, View, ViewContext, ViewHandle, WeakViewHandle,
+    Element, Entity, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
 };
 use settings::Settings;
 
@@ -21,7 +21,7 @@ pub trait ToolbarItemView: View {
         current_location
     }
 
-    fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut AppContext) {}
+    fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
 }
 
 trait ToolbarItemViewHandle {
@@ -30,9 +30,9 @@ trait ToolbarItemViewHandle {
     fn set_active_pane_item(
         &self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> ToolbarItemLocation;
-    fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut AppContext);
+    fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext);
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -263,7 +263,7 @@ impl Toolbar {
         }
     }
 
-    pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut AppContext) {
+    pub fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut ViewContext<Self>) {
         for (toolbar_item, _) in self.items.iter_mut() {
             toolbar_item.pane_focus_update(pane_focused, cx);
         }
@@ -292,14 +292,14 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for ViewHandle<T> {
     fn set_active_pane_item(
         &self,
         active_pane_item: Option<&dyn ItemHandle>,
-        cx: &mut AppContext,
+        cx: &mut WindowContext,
     ) -> ToolbarItemLocation {
         self.update(cx, |this, cx| {
             this.set_active_pane_item(active_pane_item, cx)
         })
     }
 
-    fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut AppContext) {
+    fn pane_focus_update(&mut self, pane_focused: bool, cx: &mut WindowContext) {
         self.update(cx, |this, cx| this.pane_focus_update(pane_focused, cx));
     }
 }

crates/workspace/src/workspace.rs 🔗

@@ -397,20 +397,18 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
                 .await
                 .context("Failed to create CLI symlink");
 
-            cx.update(|cx| {
-                workspace.update(cx, |workspace, cx| {
-                    if matches!(err, Err(_)) {
-                        err.notify_err(workspace, cx);
-                    } else {
-                        workspace.show_notification(1, cx, |cx| {
-                            cx.add_view(|_| {
-                                MessageNotification::new_message(
-                                    "Successfully installed the `zed` binary",
-                                )
-                            })
-                        });
-                    }
-                })
+            workspace.update(&mut cx, |workspace, cx| {
+                if matches!(err, Err(_)) {
+                    err.notify_err(workspace, cx);
+                } else {
+                    workspace.show_notification(1, cx, |cx| {
+                        cx.add_view(|_| {
+                            MessageNotification::new_message(
+                                "Successfully installed the `zed` binary",
+                            )
+                        })
+                    });
+                }
             })
         })
         .detach();
@@ -724,11 +722,9 @@ impl Workspace {
                 Stream::map(current_user, drop).merge(Stream::map(connection_status, drop));
 
             while stream.recv().await.is_some() {
-                cx.update(|cx| {
-                    if let Some(this) = this.upgrade(cx) {
-                        this.update(cx, |_, cx| cx.notify());
-                    }
-                })
+                if let Some(this) = this.upgrade(&cx) {
+                    this.update(&mut cx, |_, cx| cx.notify());
+                }
             }
         });
         let handle = cx.handle();

crates/zed/src/zed.rs 🔗

@@ -1012,12 +1012,11 @@ mod tests {
         let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
 
         // Open a file within an existing worktree.
-        cx.update(|cx| {
-            workspace.update(cx, |view, cx| {
+        workspace
+            .update(cx, |view, cx| {
                 view.open_paths(vec!["/dir1/a.txt".into()], true, cx)
             })
-        })
-        .await;
+            .await;
         cx.read(|cx| {
             assert_eq!(
                 workspace
@@ -1036,12 +1035,11 @@ mod tests {
         });
 
         // Open a file outside of any existing worktree.
-        cx.update(|cx| {
-            workspace.update(cx, |view, cx| {
+        workspace
+            .update(cx, |view, cx| {
                 view.open_paths(vec!["/dir2/b.txt".into()], true, cx)
             })
-        })
-        .await;
+            .await;
         cx.read(|cx| {
             let worktree_roots = workspace
                 .read(cx)
@@ -1072,12 +1070,11 @@ mod tests {
         });
 
         // Ensure opening a directory and one of its children only adds one worktree.
-        cx.update(|cx| {
-            workspace.update(cx, |view, cx| {
+        workspace
+            .update(cx, |view, cx| {
                 view.open_paths(vec!["/dir3".into(), "/dir3/c.txt".into()], true, cx)
             })
-        })
-        .await;
+            .await;
         cx.read(|cx| {
             let worktree_roots = workspace
                 .read(cx)
@@ -1108,12 +1105,11 @@ mod tests {
         });
 
         // Ensure opening invisibly a file outside an existing worktree adds a new, invisible worktree.
-        cx.update(|cx| {
-            workspace.update(cx, |view, cx| {
+        workspace
+            .update(cx, |view, cx| {
                 view.open_paths(vec!["/d.txt".into()], false, cx)
             })
-        })
-        .await;
+            .await;
         cx.read(|cx| {
             let worktree_roots = workspace
                 .read(cx)
@@ -1171,19 +1167,18 @@ mod tests {
         let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
 
         // Open a file within an existing worktree.
-        cx.update(|cx| {
-            workspace.update(cx, |view, cx| {
+        workspace
+            .update(cx, |view, cx| {
                 view.open_paths(vec![PathBuf::from("/root/a.txt")], true, cx)
             })
-        })
-        .await;
+            .await;
         let editor = cx.read(|cx| {
             let pane = workspace.read(cx).active_pane().read(cx);
             let item = pane.active_item().unwrap();
             item.downcast::<Editor>().unwrap()
         });
 
-        cx.update(|cx| editor.update(cx, |editor, cx| editor.handle_input("x", cx)));
+        editor.update(cx, |editor, cx| editor.handle_input("x", cx));
         app_state
             .fs
             .as_fake()