Get app running and test passing after gpui App+Platform restructure

Max Brunsfeld created

Change summary

gpui/examples/text.rs                  |  14 
gpui/src/app.rs                        | 325 +++++++++++++--------------
gpui/src/platform/mac/mod.rs           |   9 
gpui/src/platform/mac/platform.rs      | 200 ++++++++--------
gpui/src/platform/mod.rs               |  13 -
gpui/src/platform/test.rs              |   7 
zed/src/editor/buffer/mod.rs           |  18 
zed/src/editor/buffer_view.rs          | 206 +++++++++--------
zed/src/editor/display_map/fold_map.rs | 291 +++++++++++-------------
zed/src/editor/display_map/mod.rs      |  93 +++----
zed/src/file_finder.rs                 |  47 ++-
zed/src/main.rs                        |   8 
zed/src/workspace/mod.rs               |  28 +
zed/src/workspace/pane.rs              |   2 
zed/src/workspace/workspace.rs         |  50 +--
zed/src/workspace/workspace_view.rs    | 113 +++++----
zed/src/worktree/worktree.rs           |  58 ++--
17 files changed, 726 insertions(+), 756 deletions(-)

Detailed changes

gpui/examples/text.rs 🔗

@@ -1,7 +1,6 @@
 use gpui::{
     color::ColorU,
     fonts::{Properties, Weight},
-    platform::{current as platform, Runner},
     DebugContext, Element as _, Quad,
 };
 use log::LevelFilter;
@@ -11,13 +10,12 @@ use simplelog::SimpleLogger;
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
-    let mut app = gpui::App::new(()).unwrap();
-    platform::runner()
-        .on_finish_launching(move || {
-            app.platform().activate(true);
-            app.add_window(|_| TextView);
-        })
-        .run();
+    let app = gpui::App::new(()).unwrap();
+    app.on_finish_launching(|app| {
+        app.platform().activate(true);
+        app.add_window(|_| TextView);
+    })
+    .run();
 }
 
 struct TextView;

gpui/src/app.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     elements::ElementBox,
     executor,
     keymap::{self, Keystroke},
-    platform::{self, Platform as _, WindowOptions},
+    platform::{self, WindowOptions},
     presenter::Presenter,
     util::post_inc,
     AssetCache, AssetSource, FontCache, TextLayoutCache,
@@ -84,33 +84,10 @@ pub enum MenuItem<'a> {
 #[derive(Clone)]
 pub struct App(Rc<RefCell<MutableAppContext>>);
 
-pub trait TestClosure<'a, T> {
-    type Result: 'a + Future<Output = T>;
-    fn run_test(self, ctx: &'a mut MutableAppContext) -> Self::Result;
-}
-
-impl<'a, F, R, T> TestClosure<'a, T> for F
-where
-    F: FnOnce(&mut MutableAppContext) -> R,
-    R: 'a + Future<Output = T>,
-{
-    type Result = R;
-
-    fn run_test(self, ctx: &'a mut MutableAppContext) -> Self::Result {
-        (self)(ctx)
-    }
-}
-
 impl App {
-    pub fn test<
-        T,
-        A: AssetSource,
-        // F: 'static + ,
-        // G: for<'a> FnOnce(&'a mut MutableAppContext) -> impl Future<Output = T>,
-        G: for<'a> TestClosure<'a, T>,
-    >(
+    pub fn test<T, A: AssetSource, F: FnOnce(&mut MutableAppContext) -> T>(
         asset_source: A,
-        f: G,
+        f: F,
     ) -> T {
         let platform = platform::test::platform();
         let foreground = Rc::new(executor::Foreground::test());
@@ -121,11 +98,35 @@ impl App {
         )));
         ctx.borrow_mut().weak_self = Some(Rc::downgrade(&ctx));
         let mut ctx = ctx.borrow_mut();
-        smol::block_on(foreground.run(f.run_test(&mut *ctx)))
+        f(&mut *ctx)
+    }
+
+    pub fn test_async<'a, T, F, A: AssetSource, Fn>(asset_source: A, f: Fn) -> T
+    where
+        Fn: FnOnce(&'a mut MutableAppContext) -> F,
+        F: Future<Output = T> + 'a,
+    {
+        let platform = platform::test::platform();
+        let foreground = Rc::new(executor::Foreground::test());
+        let ctx = Rc::new(RefCell::new(MutableAppContext::new(
+            foreground.clone(),
+            Arc::new(platform),
+            asset_source,
+        )));
+        let mut ctx_ref = ctx.borrow_mut();
+        ctx_ref.weak_self = Some(Rc::downgrade(&ctx));
+        let ctx = &mut *ctx_ref;
+
+        // TODO - is there a better way of getting this to compile?
+        let ctx = unsafe { std::mem::transmute(ctx) };
+        let future = f(ctx);
+
+        drop(ctx_ref);
+        smol::block_on(foreground.run(future))
     }
 
     pub fn new(asset_source: impl AssetSource) -> Result<Self> {
-        let platform = Arc::new(platform::current::app());
+        let platform = platform::current::platform();
         let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?);
         let app = Self(Rc::new(RefCell::new(MutableAppContext::new(
             foreground,
@@ -199,7 +200,7 @@ impl App {
         self
     }
 
-    pub fn run<F>(self, callback: F)
+    pub fn on_finish_launching<F>(self, callback: F) -> Self
     where
         F: 'static + FnOnce(&mut MutableAppContext),
     {
@@ -207,7 +208,16 @@ impl App {
         self.0
             .borrow()
             .platform
-            .run(Box::new(move || callback(&mut *ctx.borrow_mut())));
+            .on_finish_launching(Box::new(move || callback(&mut *ctx.borrow_mut())));
+        self
+    }
+
+    pub fn set_menus(&self, menus: &[Menu]) {
+        self.0.borrow().platform.set_menus(menus);
+    }
+
+    pub fn run(self) {
+        platform::current::run();
     }
 
     pub fn on_window_invalidated<F: 'static + FnMut(WindowInvalidation, &mut MutableAppContext)>(
@@ -246,7 +256,7 @@ impl App {
         name: &str,
         arg: T,
     ) {
-        self.0.borrow_mut().dispatch_action(
+        self.0.borrow_mut().dispatch_action_any(
             window_id,
             &responder_chain,
             name,
@@ -280,15 +290,6 @@ impl App {
         handle
     }
 
-    fn read_model<T, F, S>(&self, handle: &ModelHandle<T>, read: F) -> S
-    where
-        T: Entity,
-        F: FnOnce(&T, &AppContext) -> S,
-    {
-        let state = self.0.borrow();
-        read(state.model(handle), &state.ctx)
-    }
-
     pub fn add_window<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>)
     where
         T: View,
@@ -345,15 +346,6 @@ impl App {
         result
     }
 
-    fn read_view<T, F, S>(&self, handle: &ViewHandle<T>, read: F) -> S
-    where
-        T: View,
-        F: FnOnce(&T, &AppContext) -> S,
-    {
-        let state = self.0.borrow();
-        read(state.view(handle), state.downgrade())
-    }
-
     pub fn finish_pending_tasks(&self) -> impl Future<Output = ()> {
         self.0.borrow().finish_pending_tasks()
     }
@@ -478,6 +470,10 @@ impl MutableAppContext {
         self.platform.clone()
     }
 
+    pub fn font_cache(&self) -> &Arc<FontCache> {
+        &self.font_cache
+    }
+
     pub fn foreground_executor(&self) -> &Rc<executor::Foreground> {
         &self.foreground
     }
@@ -598,7 +594,24 @@ impl MutableAppContext {
         self.ctx.render_views(window_id)
     }
 
-    pub fn dispatch_action(
+    pub fn update<T, F: FnOnce() -> T>(&mut self, callback: F) -> T {
+        self.pending_flushes += 1;
+        let result = callback();
+        self.flush_effects();
+        result
+    }
+
+    pub fn dispatch_action<T: 'static + Any>(
+        &mut self,
+        window_id: usize,
+        responder_chain: Vec<usize>,
+        name: &str,
+        arg: T,
+    ) {
+        self.dispatch_action_any(window_id, &responder_chain, name, Box::new(arg).as_ref());
+    }
+
+    fn dispatch_action_any(
         &mut self,
         window_id: usize,
         path: &[usize],
@@ -709,7 +722,7 @@ impl MutableAppContext {
                 MatchResult::None => {}
                 MatchResult::Pending => pending = true,
                 MatchResult::Action { name, arg } => {
-                    if self.dispatch_action(
+                    if self.dispatch_action_any(
                         window_id,
                         &responder_chain[0..=i],
                         &name,
@@ -796,7 +809,7 @@ impl MutableAppContext {
                                 .borrow_mut()
                                 .dispatch_event(event, ctx.downgrade());
                             for action in actions {
-                                ctx.dispatch_action(
+                                ctx.dispatch_action_any(
                                     window_id,
                                     &action.path,
                                     action.name,
@@ -1328,6 +1341,12 @@ impl UpdateView for MutableAppContext {
     }
 }
 
+impl AsRef<AppContext> for MutableAppContext {
+    fn as_ref(&self) -> &AppContext {
+        &self.ctx
+    }
+}
+
 pub struct AppContext {
     models: HashMap<usize, Box<dyn AnyModel>>,
     windows: HashMap<usize, Window>,
@@ -2112,13 +2131,6 @@ impl<T: View> ViewHandle<T> {
         app.view(self)
     }
 
-    pub fn read<'a, F, S>(&self, app: &'a App, read: F) -> S
-    where
-        F: FnOnce(&T, &AppContext) -> S,
-    {
-        app.read_view(self, read)
-    }
-
     pub fn update<A, F, S>(&self, app: &mut A, update: F) -> S
     where
         A: UpdateView,
@@ -2452,10 +2464,10 @@ mod tests {
             }
         }
 
-        App::test((), |app: &mut MutableAppContext| async move {
+        App::test((), |app| {
             let handle_1 = app.add_model(|ctx| Model::new(None, ctx));
             let handle_2 = app.add_model(|ctx| Model::new(Some(handle_1.clone()), ctx));
-            assert_eq!(app.0.borrow().ctx.models.len(), 2);
+            assert_eq!(app.ctx.models.len(), 2);
 
             handle_1.update(app, |model, ctx| {
                 model.events.push("updated".into());
@@ -2478,11 +2490,10 @@ mod tests {
                 model.other.take();
             });
 
-            let app_state = app.0.borrow();
-            assert_eq!(app_state.ctx.models.len(), 1);
-            assert!(app_state.subscriptions.is_empty());
-            assert!(app_state.observations.is_empty());
-        })
+            assert_eq!(app.ctx.models.len(), 1);
+            assert!(app.subscriptions.is_empty());
+            assert!(app.observations.is_empty());
+        });
     }
 
     #[test]
@@ -2496,8 +2507,7 @@ mod tests {
             type Event = usize;
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
+        App::test((), |app| {
             let handle_1 = app.add_model(|_| Model::default());
             let handle_2 = app.add_model(|_| Model::default());
             let handle_2b = handle_2.clone();
@@ -2513,10 +2523,10 @@ mod tests {
             });
 
             handle_2.update(app, |_, c| c.emit(7));
-            handle_1.read(app, |model, _| assert_eq!(model.events, vec![7]));
+            assert_eq!(handle_1.as_ref(app).events, vec![7]);
 
             handle_2.update(app, |_, c| c.emit(5));
-            handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5]));
+            assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]);
         })
     }
 
@@ -2532,8 +2542,7 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
+        App::test((), |app| {
             let handle_1 = app.add_model(|_| Model::default());
             let handle_2 = app.add_model(|_| Model::default());
             let handle_2b = handle_2.clone();
@@ -2551,13 +2560,13 @@ mod tests {
                 model.count = 7;
                 c.notify()
             });
-            handle_1.read(app, |model, _| assert_eq!(model.events, vec![7]));
+            assert_eq!(handle_1.as_ref(app).events, vec![7]);
 
             handle_2.update(app, |model, c| {
                 model.count = 5;
                 c.notify()
             });
-            handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5]))
+            assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5])
         })
     }
 
@@ -2572,25 +2581,25 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
+        App::test_async((), |app| async move {
             let handle = app.add_model(|_| Model::default());
             handle
-                .update(&mut app, |_, c| {
+                .update(app, |_, c| {
                     c.spawn(async { 7 }, |model, output, _| {
                         model.count = output;
                     })
                 })
                 .await;
-            handle.read(&app, |model, _| assert_eq!(model.count, 7));
+            assert_eq!(handle.as_ref(app).count, 7);
 
             handle
-                .update(&mut app, |_, c| {
+                .update(app, |_, c| {
                     c.spawn(async { 14 }, |model, output, _| {
                         model.count = output;
                     })
                 })
                 .await;
-            handle.read(&app, |model, _| assert_eq!(model.count, 14));
+            assert_eq!(handle.as_ref(app).count, 14);
         });
     }
 
@@ -2605,10 +2614,10 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
+        App::test_async((), |app| async move {
             let handle = app.add_model(|_| Model::default());
             handle
-                .update(&mut app, |_, c| {
+                .update(app, |_, c| {
                     c.spawn_stream(
                         smol::stream::iter(vec![1, 2, 3]),
                         |model, output, _| {
@@ -2620,10 +2629,7 @@ mod tests {
                     )
                 })
                 .await;
-
-            handle.read(&app, |model, _| {
-                assert_eq!(model.events, [Some(1), Some(2), Some(3), None])
-            });
+            assert_eq!(handle.as_ref(app).events, [Some(1), Some(2), Some(3), None])
         })
     }
 
@@ -2662,40 +2668,34 @@ mod tests {
             }
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
+        App::test((), |app| {
             let (window_id, _) = app.add_window(|ctx| View::new(None, ctx));
             let handle_1 = app.add_view(window_id, |ctx| View::new(None, ctx));
             let handle_2 = app.add_view(window_id, |ctx| View::new(Some(handle_1.clone()), ctx));
-            assert_eq!(app.0.borrow().ctx.windows[&window_id].views.len(), 3);
+            assert_eq!(app.ctx.windows[&window_id].views.len(), 3);
 
             handle_1.update(app, |view, ctx| {
                 view.events.push("updated".into());
                 ctx.emit(1);
                 ctx.emit(2);
             });
-            handle_1.read(app, |view, _| {
-                assert_eq!(view.events, vec!["updated".to_string()]);
-            });
-            handle_2.read(app, |view, _| {
-                assert_eq!(
-                    view.events,
-                    vec![
-                        "observed event 1".to_string(),
-                        "observed event 2".to_string(),
-                    ]
-                );
-            });
+            assert_eq!(handle_1.as_ref(app).events, vec!["updated".to_string()]);
+            assert_eq!(
+                handle_2.as_ref(app).events,
+                vec![
+                    "observed event 1".to_string(),
+                    "observed event 2".to_string(),
+                ]
+            );
 
             handle_2.update(app, |view, _| {
                 drop(handle_1);
                 view.other.take();
             });
 
-            let app_state = app.0.borrow();
-            assert_eq!(app_state.ctx.windows[&window_id].views.len(), 2);
-            assert!(app_state.subscriptions.is_empty());
-            assert!(app_state.observations.is_empty());
+            assert_eq!(app.ctx.windows[&window_id].views.len(), 2);
+            assert!(app.subscriptions.is_empty());
+            assert!(app.observations.is_empty());
         })
     }
 
@@ -2726,8 +2726,7 @@ mod tests {
             type Event = usize;
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
+        App::test((), |app| {
             let (window_id, handle_1) = app.add_window(|_| View::default());
             let handle_2 = app.add_view(window_id, |_| View::default());
             let handle_2b = handle_2.clone();
@@ -2748,13 +2747,13 @@ mod tests {
             });
 
             handle_2.update(app, |_, c| c.emit(7));
-            handle_1.read(app, |view, _| assert_eq!(view.events, vec![7]));
+            assert_eq!(handle_1.as_ref(app).events, vec![7]);
 
             handle_2.update(app, |_, c| c.emit(5));
-            handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5]));
+            assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5]);
 
             handle_3.update(app, |_, c| c.emit(9));
-            handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5, 9]));
+            assert_eq!(handle_1.as_ref(app).events, vec![7, 10, 5, 9]);
         })
     }
 
@@ -2782,9 +2781,7 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
-
+        App::test((), |app| {
             let (window_id, _) = app.add_window(|_| View);
             let observing_view = app.add_view(window_id, |_| View);
             let emitting_view = app.add_view(window_id, |_| View);
@@ -2799,7 +2796,7 @@ mod tests {
                 ctx.subscribe(&observed_model, |_, _, _| {});
             });
 
-            app.update(|_| {
+            app.update(|| {
                 drop(observing_view);
                 drop(observing_model);
             });
@@ -2839,8 +2836,7 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
+        App::test((), |app| {
             let (_, view) = app.add_window(|_| View::default());
             let model = app.add_model(|_| Model::default());
 
@@ -2854,7 +2850,7 @@ mod tests {
                 model.count = 11;
                 c.notify();
             });
-            view.read(app, |view, _| assert_eq!(view.events, vec![11]));
+            assert_eq!(view.as_ref(app).events, vec![11]);
         })
     }
 
@@ -2882,9 +2878,7 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
-
+        App::test((), |app| {
             let (window_id, _) = app.add_window(|_| View);
             let observing_view = app.add_view(window_id, |_| View);
             let observing_model = app.add_model(|_| Model);
@@ -2897,7 +2891,7 @@ mod tests {
                 ctx.observe(&observed_model, |_, _, _| {});
             });
 
-            app.update(|_| {
+            app.update(|| {
                 drop(observing_view);
                 drop(observing_model);
             });
@@ -2937,8 +2931,7 @@ mod tests {
             }
         }
 
-        App::test((), |mut app| async move {
-            let app = &mut app;
+        App::test((), |app| {
             let (window_id, view_1) = app.add_window(|_| View::default());
             let view_2 = app.add_view(window_id, |_| View::default());
 
@@ -2953,18 +2946,16 @@ mod tests {
                 ctx.focus(&view_1);
             });
 
-            view_1.read(app, |view_1, _| {
-                assert_eq!(
-                    view_1.events,
-                    [
-                        "self focused".to_string(),
-                        "self blurred".to_string(),
-                        "view 2 focused".to_string(),
-                        "self focused".to_string(),
-                        "view 2 blurred".to_string(),
-                    ],
-                );
-            });
+            assert_eq!(
+                view_1.as_ref(app).events,
+                [
+                    "self focused".to_string(),
+                    "self blurred".to_string(),
+                    "view 2 focused".to_string(),
+                    "self focused".to_string(),
+                    "view 2 blurred".to_string(),
+                ],
+            );
         })
     }
 
@@ -2989,24 +2980,24 @@ mod tests {
             }
         }
 
-        App::test((), |mut app| async move {
+        App::test_async((), |app| async move {
             let (_, handle) = app.add_window(|_| View::default());
             handle
-                .update(&mut app, |_, c| {
+                .update(app, |_, c| {
                     c.spawn(async { 7 }, |me, output, _| {
                         me.count = output;
                     })
                 })
                 .await;
-            handle.read(&app, |view, _| assert_eq!(view.count, 7));
+            assert_eq!(handle.as_ref(app).count, 7);
             handle
-                .update(&mut app, |_, c| {
+                .update(app, |_, c| {
                     c.spawn(async { 14 }, |me, output, _| {
                         me.count = output;
                     })
                 })
                 .await;
-            handle.read(&app, |view, _| assert_eq!(view.count, 14));
+            assert_eq!(handle.as_ref(app).count, 14);
         });
     }
 
@@ -3031,10 +3022,10 @@ mod tests {
             }
         }
 
-        App::test((), |mut app| async move {
+        App::test_async((), |app| async move {
             let (_, handle) = app.add_window(|_| View::default());
             handle
-                .update(&mut app, |_, c| {
+                .update(app, |_, c| {
                     c.spawn_stream(
                         smol::stream::iter(vec![1_usize, 2, 3]),
                         |me, output, _| {
@@ -3047,9 +3038,7 @@ mod tests {
                 })
                 .await;
 
-            handle.read(&app, |view, _| {
-                assert_eq!(view.events, [Some(1), Some(2), Some(3), None])
-            });
+            assert_eq!(handle.as_ref(app).events, [Some(1), Some(2), Some(3), None])
         });
     }
 
@@ -3095,7 +3084,7 @@ mod tests {
             foo: String,
         }
 
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let actions = Rc::new(RefCell::new(Vec::new()));
 
             let actions_clone = actions.clone();
@@ -3169,7 +3158,7 @@ mod tests {
     }
 
     #[test]
-    fn test_dispatch_keystroke() -> Result<()> {
+    fn test_dispatch_keystroke() {
         use std::cell::Cell;
 
         #[derive(Clone)]
@@ -3209,7 +3198,7 @@ mod tests {
             }
         }
 
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let mut view_1 = View::new(1);
             let mut view_2 = View::new(2);
             let mut view_3 = View::new(3);
@@ -3238,12 +3227,12 @@ mod tests {
             app.dispatch_keystroke(
                 window_id,
                 vec![view_1.id(), view_2.id(), view_3.id()],
-                &Keystroke::parse("a")?,
-            )?;
+                &Keystroke::parse("a").unwrap(),
+            )
+            .unwrap();
 
             assert!(handled_action.get());
-            Ok(())
-        })
+        });
     }
 
     // #[test]
@@ -3266,7 +3255,7 @@ mod tests {
     //         }
     //     }
 
-    //     App::test(|mut app| async move {
+    //     App::test(|app| async move {
     //         let (window_id, _) = app.add_window(|_| View { count: 3 });
     //         let view_1 = app.add_view(window_id, |_| View { count: 1 });
     //         let view_2 = app.add_view(window_id, |_| View { count: 2 });
@@ -3293,7 +3282,7 @@ mod tests {
     //         });
 
     //         let view_2_id = view_2.id();
-    //         view_1.update(&mut app, |view, ctx| {
+    //         view_1.update(app, |view, ctx| {
     //             view.count = 7;
     //             ctx.notify();
     //             drop(view_2);
@@ -3304,7 +3293,7 @@ mod tests {
     //         assert!(invalidation.updated.contains(&view_1.id()));
     //         assert_eq!(invalidation.removed, vec![view_2_id]);
 
-    //         let view_3 = view_1.update(&mut app, |_, ctx| ctx.add_view(|_| View { count: 8 }));
+    //         let view_3 = view_1.update(app, |_, ctx| ctx.add_view(|_| View { count: 8 }));
 
     //         let invalidation = window_invalidations.borrow_mut().drain(..).next().unwrap();
     //         assert_eq!(invalidation.updated.len(), 1);
@@ -3312,7 +3301,7 @@ mod tests {
     //         assert!(invalidation.removed.is_empty());
 
     //         view_3
-    //             .update(&mut app, |_, ctx| {
+    //             .update(app, |_, ctx| {
     //                 ctx.spawn_local(async { 9 }, |me, output, ctx| {
     //                     me.count = output;
     //                     ctx.notify();
@@ -3351,49 +3340,49 @@ mod tests {
             type Event = ();
         }
 
-        App::test((), |mut app| async move {
+        App::test_async((), |app| async move {
             let model = app.add_model(|_| Model);
             let (_, view) = app.add_window(|_| View);
 
-            model.update(&mut app, |_, ctx| {
+            model.update(app, |_, ctx| {
                 ctx.spawn(async {}, |_, _, _| {}).detach();
                 // Cancel this task
                 drop(ctx.spawn(async {}, |_, _, _| {}));
             });
 
-            view.update(&mut app, |_, ctx| {
+            view.update(app, |_, ctx| {
                 ctx.spawn(async {}, |_, _, _| {}).detach();
                 // Cancel this task
                 drop(ctx.spawn(async {}, |_, _, _| {}));
             });
 
-            assert!(!app.0.borrow().future_handlers.borrow().is_empty());
+            assert!(!app.future_handlers.borrow().is_empty());
             app.finish_pending_tasks().await;
-            assert!(app.0.borrow().future_handlers.borrow().is_empty());
+            assert!(app.future_handlers.borrow().is_empty());
             app.finish_pending_tasks().await; // Don't block if there are no tasks
 
-            model.update(&mut app, |_, ctx| {
+            model.update(app, |_, ctx| {
                 ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {})
                     .detach();
                 // Cancel this task
                 drop(ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {}));
             });
 
-            view.update(&mut app, |_, ctx| {
+            view.update(app, |_, ctx| {
                 ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {})
                     .detach();
                 // Cancel this task
                 drop(ctx.spawn_stream(smol::stream::iter(vec![1, 2, 3]), |_, _, _| {}, |_, _| {}));
             });
 
-            assert!(!app.0.borrow().stream_handlers.borrow().is_empty());
+            assert!(!app.stream_handlers.borrow().is_empty());
             app.finish_pending_tasks().await;
-            assert!(app.0.borrow().stream_handlers.borrow().is_empty());
+            assert!(app.stream_handlers.borrow().is_empty());
             app.finish_pending_tasks().await; // Don't block if there are no tasks
 
             // Tasks are considered finished when we drop handles
             let mut tasks = Vec::new();
-            model.update(&mut app, |_, ctx| {
+            model.update(app, |_, ctx| {
                 tasks.push(Box::new(ctx.spawn(async {}, |_, _, _| {})));
                 tasks.push(Box::new(ctx.spawn_stream(
                     smol::stream::iter(vec![1, 2, 3]),
@@ -3402,7 +3391,7 @@ mod tests {
                 )));
             });
 
-            view.update(&mut app, |_, ctx| {
+            view.update(app, |_, ctx| {
                 tasks.push(Box::new(ctx.spawn(async {}, |_, _, _| {})));
                 tasks.push(Box::new(ctx.spawn_stream(
                     smol::stream::iter(vec![1, 2, 3]),
@@ -3411,12 +3400,12 @@ mod tests {
                 )));
             });
 
-            assert!(!app.0.borrow().stream_handlers.borrow().is_empty());
+            assert!(!app.stream_handlers.borrow().is_empty());
 
             let finish_pending_tasks = app.finish_pending_tasks();
             drop(tasks);
             finish_pending_tasks.await;
-            assert!(app.0.borrow().stream_handlers.borrow().is_empty());
+            assert!(app.stream_handlers.borrow().is_empty());
             app.finish_pending_tasks().await; // Don't block if there are no tasks
         });
     }

gpui/src/platform/mac/mod.rs 🔗

@@ -5,7 +5,6 @@ mod fonts;
 mod geometry;
 mod platform;
 mod renderer;
-mod runner;
 mod sprite_cache;
 mod window;
 
@@ -13,15 +12,15 @@ use cocoa::base::{BOOL, NO, YES};
 pub use dispatcher::Dispatcher;
 pub use fonts::FontSystem;
 use platform::MacPlatform;
-pub use runner::Runner;
+use std::sync::Arc;
 use window::Window;
 
-pub fn app() -> impl super::Platform {
+pub fn platform() -> Arc<dyn super::Platform> {
     MacPlatform::new()
 }
 
-pub fn runner() -> impl super::Runner {
-    Runner::new()
+pub fn run() {
+    MacPlatform::run();
 }
 
 trait BoolExt {

gpui/src/platform/mac/platform.rs 🔗

@@ -90,14 +90,113 @@ struct Callbacks {
 }
 
 impl MacPlatform {
-    pub fn new() -> Self {
-        Self {
+    pub fn new() -> Arc<dyn platform::Platform> {
+        let result = Arc::new(Self {
             dispatcher: Arc::new(Dispatcher),
             fonts: Arc::new(FontSystem::new()),
             callbacks: Default::default(),
             menu_item_actions: Default::default(),
+        });
+
+        unsafe {
+            let app: id = msg_send![APP_CLASS, sharedApplication];
+            let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new];
+            let self_ptr = result.as_ref() as *const Self as *const c_void;
+            app.setDelegate_(app_delegate);
+            (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr);
+            (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr);
+        }
+
+        result
+    }
+
+    pub fn run() {
+        unsafe {
+            let pool = NSAutoreleasePool::new(nil);
+            let app: id = msg_send![APP_CLASS, sharedApplication];
+
+            app.run();
+            pool.drain();
+            (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::<c_void>());
+            (*app.delegate()).set_ivar(MAC_PLATFORM_IVAR, null_mut::<c_void>());
         }
     }
+
+    unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id {
+        let menu_bar = NSMenu::new(nil).autorelease();
+        let mut menu_item_actions = self.menu_item_actions.borrow_mut();
+        menu_item_actions.clear();
+
+        for menu_config in menus {
+            let menu_bar_item = NSMenuItem::new(nil).autorelease();
+            let menu = NSMenu::new(nil).autorelease();
+
+            menu.setTitle_(ns_string(menu_config.name));
+
+            for item_config in menu_config.items {
+                let item;
+
+                match item_config {
+                    MenuItem::Separator => {
+                        item = NSMenuItem::separatorItem(nil);
+                    }
+                    MenuItem::Action {
+                        name,
+                        keystroke,
+                        action,
+                    } => {
+                        if let Some(keystroke) = keystroke {
+                            let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
+                                panic!(
+                                    "Invalid keystroke for menu item {}:{} - {:?}",
+                                    menu_config.name, name, err
+                                )
+                            });
+
+                            let mut mask = NSEventModifierFlags::empty();
+                            for (modifier, flag) in &[
+                                (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
+                                (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
+                                (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
+                            ] {
+                                if *modifier {
+                                    mask |= *flag;
+                                }
+                            }
+
+                            item = NSMenuItem::alloc(nil)
+                                .initWithTitle_action_keyEquivalent_(
+                                    ns_string(name),
+                                    selector("handleGPUIMenuItem:"),
+                                    ns_string(&keystroke.key),
+                                )
+                                .autorelease();
+                            item.setKeyEquivalentModifierMask_(mask);
+                        } else {
+                            item = NSMenuItem::alloc(nil)
+                                .initWithTitle_action_keyEquivalent_(
+                                    ns_string(name),
+                                    selector("handleGPUIMenuItem:"),
+                                    ns_string(""),
+                                )
+                                .autorelease();
+                        }
+
+                        let tag = menu_item_actions.len() as NSInteger;
+                        let _: () = msg_send![item, setTag: tag];
+                        menu_item_actions.push(action.to_string());
+                    }
+                }
+
+                menu.addItem_(item);
+            }
+
+            menu_bar_item.setSubmenu_(menu);
+            menu_bar.addItem_(menu_bar_item);
+        }
+
+        menu_bar
+    }
 }
 
 impl platform::Platform for MacPlatform {
@@ -121,23 +220,8 @@ impl platform::Platform for MacPlatform {
         self.callbacks.borrow_mut().open_files = Some(callback);
     }
 
-    fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>) {
-        self.callbacks.borrow_mut().finish_launching = Some(on_finish_launching);
-
-        unsafe {
-            let pool = NSAutoreleasePool::new(nil);
-            let app: id = msg_send![APP_CLASS, sharedApplication];
-            let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new];
-
-            let self_ptr = self as *const Self as *mut c_void;
-            (*app).set_ivar(MAC_PLATFORM_IVAR, self_ptr);
-            (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, self_ptr);
-            app.setDelegate_(app_delegate);
-            app.run();
-            pool.drain();
-            (*app).set_ivar(MAC_PLATFORM_IVAR, null_mut::<c_void>());
-            (*app_delegate).set_ivar(MAC_PLATFORM_IVAR, null_mut::<c_void>());
-        }
+    fn on_finish_launching(&self, callback: Box<dyn FnOnce() -> ()>) {
+        self.callbacks.borrow_mut().finish_launching = Some(callback);
     }
 
     fn dispatcher(&self) -> Arc<dyn platform::Dispatcher> {
@@ -222,84 +306,6 @@ impl platform::Platform for MacPlatform {
     }
 }
 
-impl MacPlatform {
-    unsafe fn create_menu_bar(&self, menus: &[Menu]) -> id {
-        let menu_bar = NSMenu::new(nil).autorelease();
-        let mut menu_item_actions = self.menu_item_actions.borrow_mut();
-        menu_item_actions.clear();
-
-        for menu_config in menus {
-            let menu_bar_item = NSMenuItem::new(nil).autorelease();
-            let menu = NSMenu::new(nil).autorelease();
-
-            menu.setTitle_(ns_string(menu_config.name));
-
-            for item_config in menu_config.items {
-                let item;
-
-                match item_config {
-                    MenuItem::Separator => {
-                        item = NSMenuItem::separatorItem(nil);
-                    }
-                    MenuItem::Action {
-                        name,
-                        keystroke,
-                        action,
-                    } => {
-                        if let Some(keystroke) = keystroke {
-                            let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
-                                panic!(
-                                    "Invalid keystroke for menu item {}:{} - {:?}",
-                                    menu_config.name, name, err
-                                )
-                            });
-
-                            let mut mask = NSEventModifierFlags::empty();
-                            for (modifier, flag) in &[
-                                (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
-                                (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
-                                (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
-                            ] {
-                                if *modifier {
-                                    mask |= *flag;
-                                }
-                            }
-
-                            item = NSMenuItem::alloc(nil)
-                                .initWithTitle_action_keyEquivalent_(
-                                    ns_string(name),
-                                    selector("handleGPUIMenuItem:"),
-                                    ns_string(&keystroke.key),
-                                )
-                                .autorelease();
-                            item.setKeyEquivalentModifierMask_(mask);
-                        } else {
-                            item = NSMenuItem::alloc(nil)
-                                .initWithTitle_action_keyEquivalent_(
-                                    ns_string(name),
-                                    selector("handleGPUIMenuItem:"),
-                                    ns_string(""),
-                                )
-                                .autorelease();
-                        }
-
-                        let tag = menu_item_actions.len() as NSInteger;
-                        let _: () = msg_send![item, setTag: tag];
-                        menu_item_actions.push(action.to_string());
-                    }
-                }
-
-                menu.addItem_(item);
-            }
-
-            menu_bar_item.setSubmenu_(menu);
-            menu_bar.addItem_(menu_bar_item);
-        }
-
-        menu_bar
-    }
-}
-
 unsafe fn get_platform(object: &mut Object) -> &MacPlatform {
     let platform_ptr: *mut c_void = *object.get_ivar(MAC_PLATFORM_IVAR);
     assert!(!platform_ptr.is_null());

gpui/src/platform/mod.rs 🔗

@@ -22,24 +22,13 @@ use async_task::Runnable;
 pub use event::Event;
 use std::{ops::Range, path::PathBuf, rc::Rc, sync::Arc};
 
-pub trait Runner {
-    fn on_finish_launching<F: 'static + FnOnce()>(self, callback: F) -> Self;
-    fn on_menu_command<F: 'static + FnMut(&str)>(self, callback: F) -> Self;
-    fn on_become_active<F: 'static + FnMut()>(self, callback: F) -> Self;
-    fn on_resign_active<F: 'static + FnMut()>(self, callback: F) -> Self;
-    fn on_event<F: 'static + FnMut(Event) -> bool>(self, callback: F) -> Self;
-    fn on_open_files<F: 'static + FnMut(Vec<PathBuf>)>(self, callback: F) -> Self;
-    fn set_menus(self, menus: &[Menu]) -> Self;
-    fn run(self);
-}
-
 pub trait Platform {
     fn on_menu_command(&self, callback: Box<dyn FnMut(&str)>);
     fn on_become_active(&self, callback: Box<dyn FnMut()>);
     fn on_resign_active(&self, callback: Box<dyn FnMut()>);
     fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
     fn on_open_files(&self, callback: Box<dyn FnMut(Vec<PathBuf>)>);
-    fn run(&self, on_finish_launching: Box<dyn FnOnce() -> ()>);
+    fn on_finish_launching(&self, callback: Box<dyn FnOnce() -> ()>);
 
     fn dispatcher(&self) -> Arc<dyn Dispatcher>;
     fn fonts(&self) -> Arc<dyn FontSystem>;

gpui/src/platform/test.rs 🔗

@@ -39,9 +39,7 @@ impl super::Platform for Platform {
 
     fn on_open_files(&self, _: Box<dyn FnMut(Vec<std::path::PathBuf>)>) {}
 
-    fn run(&self, _on_finish_launching: Box<dyn FnOnce() -> ()>) {
-        unimplemented!()
-    }
+    fn on_finish_launching(&self, _: Box<dyn FnOnce() -> ()>) {}
 
     fn dispatcher(&self) -> Arc<dyn super::Dispatcher> {
         self.dispatcher.clone()
@@ -61,8 +59,7 @@ impl super::Platform for Platform {
         Ok(Box::new(Window::new(options.bounds.size())))
     }
 
-    fn set_menus(&self, _menus: &[crate::Menu]) {
-    }
+    fn set_menus(&self, _menus: &[crate::Menu]) {}
 
     fn quit(&self) {}
 

zed/src/editor/buffer/mod.rs 🔗

@@ -2193,13 +2193,13 @@ mod tests {
 
     #[test]
     fn test_edit_events() {
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
             let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
 
             let buffer1 = app.add_model(|_| Buffer::new(0, "abcdef"));
             let buffer2 = app.add_model(|_| Buffer::new(1, "abcdef"));
-            let ops = buffer1.update(&mut app, |buffer, ctx| {
+            let ops = buffer1.update(app, |buffer, ctx| {
                 let buffer_1_events = buffer_1_events.clone();
                 ctx.subscribe(&buffer1, move |_, event, _| {
                     buffer_1_events.borrow_mut().push(event.clone())
@@ -2211,7 +2211,7 @@ mod tests {
 
                 buffer.edit(Some(2..4), "XYZ", Some(ctx)).unwrap()
             });
-            buffer2.update(&mut app, |buffer, ctx| {
+            buffer2.update(app, |buffer, ctx| {
                 buffer.apply_ops(ops, Some(ctx)).unwrap();
             });
 
@@ -2715,12 +2715,12 @@ mod tests {
 
     #[test]
     fn test_is_modified() -> Result<()> {
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let model = app.add_model(|_| Buffer::new(0, "abc"));
             let events = Rc::new(RefCell::new(Vec::new()));
 
             // initially, the buffer isn't dirty.
-            model.update(&mut app, |buffer, ctx| {
+            model.update(app, |buffer, ctx| {
                 ctx.subscribe(&model, {
                     let events = events.clone();
                     move |_, event, _| events.borrow_mut().push(event.clone())
@@ -2733,7 +2733,7 @@ mod tests {
             });
 
             // after the first edit, the buffer is dirty, and emits a dirtied event.
-            model.update(&mut app, |buffer, ctx| {
+            model.update(app, |buffer, ctx| {
                 assert!(buffer.text() == "ac");
                 assert!(buffer.is_dirty());
                 assert_eq!(
@@ -2752,7 +2752,7 @@ mod tests {
             });
 
             // after saving, the buffer is not dirty, and emits a saved event.
-            model.update(&mut app, |buffer, ctx| {
+            model.update(app, |buffer, ctx| {
                 assert!(!buffer.is_dirty());
                 assert_eq!(*events.borrow(), &[Event::Saved]);
                 events.borrow_mut().clear();
@@ -2762,7 +2762,7 @@ mod tests {
             });
 
             // after editing again, the buffer is dirty, and emits another dirty event.
-            model.update(&mut app, |buffer, ctx| {
+            model.update(app, |buffer, ctx| {
                 assert!(buffer.text() == "aBDc");
                 assert!(buffer.is_dirty());
                 assert_eq!(
@@ -2788,7 +2788,7 @@ mod tests {
                 assert!(buffer.is_dirty());
             });
 
-            model.update(&mut app, |_, _| {
+            model.update(app, |_, _| {
                 assert_eq!(
                     *events.borrow(),
                     &[Event::Edited(vec![Edit {

zed/src/editor/buffer_view.rs 🔗

@@ -6,7 +6,7 @@ use crate::{settings::Settings, watch, workspace};
 use anyhow::Result;
 use futures_core::future::LocalBoxFuture;
 use gpui::{
-    fonts::Properties as FontProperties, keymap::Binding, text_layout, App, AppContext, Element,
+    fonts::Properties as FontProperties, keymap::Binding, text_layout, AppContext, Element,
     ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, View, ViewContext,
     WeakViewHandle,
 };
@@ -1240,112 +1240,125 @@ mod tests {
     use super::*;
     use crate::{editor::Point, settings, test::sample_text};
     use anyhow::Error;
+    use gpui::App;
     use unindent::Unindent;
 
     #[test]
     fn test_selection_with_mouse() {
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n"));
             let settings = settings::channel(&app.font_cache()).unwrap().1;
             let (_, buffer_view) =
                 app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
 
-            buffer_view.update(&mut app, |view, ctx| {
+            buffer_view.update(app, |view, ctx| {
                 view.begin_selection(DisplayPoint::new(2, 2), false, ctx);
             });
 
-            buffer_view.read(&app, |view, app| {
-                let selections = view
-                    .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    selections,
-                    [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
-                );
-            });
+            let view = buffer_view.as_ref(app);
+            let selections = view
+                .selections_in_range(
+                    DisplayPoint::zero()..view.max_point(app.as_ref()),
+                    app.as_ref(),
+                )
+                .collect::<Vec<_>>();
+            assert_eq!(
+                selections,
+                [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
+            );
 
-            buffer_view.update(&mut app, |view, ctx| {
+            buffer_view.update(app, |view, ctx| {
                 view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
             });
 
-            buffer_view.read(&app, |view, app| {
-                let selections = view
-                    .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    selections,
-                    [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-                );
-            });
+            let view = buffer_view.as_ref(app);
+            let selections = view
+                .selections_in_range(
+                    DisplayPoint::zero()..view.max_point(app.as_ref()),
+                    app.as_ref(),
+                )
+                .collect::<Vec<_>>();
+            assert_eq!(
+                selections,
+                [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
+            );
 
-            buffer_view.update(&mut app, |view, ctx| {
+            buffer_view.update(app, |view, ctx| {
                 view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
             });
 
-            buffer_view.read(&app, |view, app| {
-                let selections = view
-                    .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    selections,
-                    [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
-                );
-            });
+            let view = buffer_view.as_ref(app);
+            let selections = view
+                .selections_in_range(
+                    DisplayPoint::zero()..view.max_point(app.as_ref()),
+                    app.as_ref(),
+                )
+                .collect::<Vec<_>>();
+            assert_eq!(
+                selections,
+                [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
+            );
 
-            buffer_view.update(&mut app, |view, ctx| {
+            buffer_view.update(app, |view, ctx| {
                 view.end_selection(ctx);
                 view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
             });
 
-            buffer_view.read(&app, |view, app| {
-                let selections = view
-                    .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    selections,
-                    [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
-                );
-            });
+            let view = buffer_view.as_ref(app);
+            let selections = view
+                .selections_in_range(
+                    DisplayPoint::zero()..view.max_point(app.as_ref()),
+                    app.as_ref(),
+                )
+                .collect::<Vec<_>>();
+            assert_eq!(
+                selections,
+                [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
+            );
 
-            buffer_view.update(&mut app, |view, ctx| {
+            buffer_view.update(app, |view, ctx| {
                 view.begin_selection(DisplayPoint::new(3, 3), true, ctx);
                 view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx);
             });
 
-            buffer_view.read(&app, |view, app| {
-                let selections = view
-                    .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    selections,
-                    [
-                        DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
-                        DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
-                    ]
-                );
-            });
+            let view = buffer_view.as_ref(app);
+            let selections = view
+                .selections_in_range(
+                    DisplayPoint::zero()..view.max_point(app.as_ref()),
+                    app.as_ref(),
+                )
+                .collect::<Vec<_>>();
+            assert_eq!(
+                selections,
+                [
+                    DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
+                    DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
+                ]
+            );
 
-            buffer_view.update(&mut app, |view, ctx| {
+            buffer_view.update(app, |view, ctx| {
                 view.end_selection(ctx);
             });
 
-            buffer_view.read(&app, |view, app| {
-                let selections = view
-                    .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
-                    .collect::<Vec<_>>();
-                assert_eq!(
-                    selections,
-                    [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
-                );
-            });
+            let view = buffer_view.as_ref(app);
+            let selections = view
+                .selections_in_range(
+                    DisplayPoint::zero()..view.max_point(app.as_ref()),
+                    app.as_ref(),
+                )
+                .collect::<Vec<_>>();
+            assert_eq!(
+                selections,
+                [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
+            );
         });
     }
 
     #[test]
-    fn test_layout_line_numbers() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_layout_line_numbers() {
+        App::test((), |app| {
             let layout_cache = TextLayoutCache::new(app.platform().fonts());
-            let font_cache = app.font_cache();
+            let font_cache = app.font_cache().clone();
 
             let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6)));
 
@@ -1353,19 +1366,17 @@ mod tests {
             let (_, view) =
                 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
 
-            view.read(&app, |view, app| {
-                let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?;
-                assert_eq!(layouts.len(), 6);
-                Result::<()>::Ok(())
-            })?;
-
-            Ok(())
+            let layouts = view
+                .as_ref(app)
+                .layout_line_numbers(1000.0, &font_cache, &layout_cache, app.as_ref())
+                .unwrap();
+            assert_eq!(layouts.len(), 6);
         })
     }
 
     #[test]
-    fn test_fold() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_fold() {
+        App::test((), |app| {
             let buffer = app.add_model(|_| {
                 Buffer::new(
                     0,
@@ -1393,8 +1404,9 @@ mod tests {
             let (_, view) =
                 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
 
-            view.update(&mut app, |view, ctx| {
-                view.select_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx)?;
+            view.update(app, |view, ctx| {
+                view.select_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx)
+                    .unwrap();
                 view.fold(&(), ctx);
                 assert_eq!(
                     view.text(ctx.app()),
@@ -1449,23 +1461,19 @@ mod tests {
 
                 view.unfold(&(), ctx);
                 assert_eq!(view.text(ctx.app()), buffer.as_ref(ctx).text());
-
-                Ok::<(), Error>(())
-            })?;
-
-            Ok(())
-        })
+            });
+        });
     }
 
     #[test]
     fn test_move_cursor() -> Result<()> {
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6)));
             let settings = settings::channel(&app.font_cache()).unwrap().1;
             let (_, view) =
                 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
 
-            buffer.update(&mut app, |buffer, ctx| {
+            buffer.update(app, |buffer, ctx| {
                 buffer.edit(
                     vec![
                         Point::new(1, 0)..Point::new(1, 0),
@@ -1476,7 +1484,7 @@ mod tests {
                 )
             })?;
 
-            view.update(&mut app, |view, ctx| {
+            view.update(app, |view, ctx| {
                 view.move_down(&(), ctx);
                 assert_eq!(
                     view.selections(ctx.app()),
@@ -1495,8 +1503,8 @@ mod tests {
     }
 
     #[test]
-    fn test_backspace() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_backspace() {
+        App::test((), |app| {
             let buffer = app.add_model(|_| {
                 Buffer::new(0, "one two three\nfour five six\nseven eight nine\nten\n")
             });
@@ -1504,7 +1512,7 @@ mod tests {
             let (_, view) =
                 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
 
-            view.update(&mut app, |view, ctx| -> Result<()> {
+            view.update(app, |view, ctx| {
                 view.select_ranges(
                     &[
                         // an empty selection - the preceding character is deleted
@@ -1515,17 +1523,15 @@ mod tests {
                         DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
                     ],
                     ctx,
-                )?;
+                )
+                .unwrap();
                 view.backspace(&(), ctx);
-                Ok(())
-            })?;
-
-            buffer.read(&mut app, |buffer, _| -> Result<()> {
-                assert_eq!(buffer.text(), "oe two three\nfou five six\nseven ten\n");
-                Ok(())
-            })?;
+            });
 
-            Ok(())
+            assert_eq!(
+                buffer.as_ref(app).text(),
+                "oe two three\nfou five six\nseven ten\n"
+            );
         })
     }
 

zed/src/editor/display_map/fold_map.rs 🔗

@@ -469,115 +469,106 @@ mod tests {
     use gpui::App;
 
     #[test]
-    fn test_basic_folds() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_basic_folds() {
+        App::test((), |app| {
             let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
-            let mut map = app.read(|app| FoldMap::new(buffer.clone(), app));
-
-            app.read(|app| {
-                map.fold(
-                    vec![
-                        Point::new(0, 2)..Point::new(2, 2),
-                        Point::new(2, 4)..Point::new(4, 1),
-                    ],
-                    app,
-                )?;
-                assert_eq!(map.text(app), "aa…cc…eeeee");
-                Ok::<(), anyhow::Error>(())
-            })?;
-
-            let edits = buffer.update(&mut app, |buffer, ctx| {
+            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+
+            map.fold(
+                vec![
+                    Point::new(0, 2)..Point::new(2, 2),
+                    Point::new(2, 4)..Point::new(4, 1),
+                ],
+                app.as_ref(),
+            )
+            .unwrap();
+            assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee");
+
+            let edits = buffer.update(app, |buffer, ctx| {
                 let start_version = buffer.version.clone();
-                buffer.edit(
-                    vec![
-                        Point::new(0, 0)..Point::new(0, 1),
-                        Point::new(2, 3)..Point::new(2, 3),
-                    ],
-                    "123",
-                    Some(ctx),
-                )?;
-                Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::<Vec<_>>())
-            })?;
-
-            app.read(|app| {
-                map.apply_edits(&edits, app)?;
-                assert_eq!(map.text(app), "123a…c123c…eeeee");
-                Ok::<(), anyhow::Error>(())
-            })?;
-
-            let edits = buffer.update(&mut app, |buffer, ctx| {
-                let start_version = buffer.version.clone();
-                buffer.edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))?;
-                Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::<Vec<_>>())
-            })?;
+                buffer
+                    .edit(
+                        vec![
+                            Point::new(0, 0)..Point::new(0, 1),
+                            Point::new(2, 3)..Point::new(2, 3),
+                        ],
+                        "123",
+                        Some(ctx),
+                    )
+                    .unwrap();
+                buffer.edits_since(start_version).collect::<Vec<_>>()
+            });
 
-            app.read(|app| {
-                map.apply_edits(&edits, app)?;
-                assert_eq!(map.text(app), "123a…c123456eee");
+            map.apply_edits(&edits, app.as_ref()).unwrap();
+            assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee");
+
+            let edits = buffer.update(app, |buffer, ctx| {
+                let start_version = buffer.version.clone();
+                buffer
+                    .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))
+                    .unwrap();
+                buffer.edits_since(start_version).collect::<Vec<_>>()
+            });
 
-                map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app)?;
-                assert_eq!(map.text(app), "123aaaaa\nbbbbbb\nccc123456eee");
+            map.apply_edits(&edits, app.as_ref()).unwrap();
+            assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
 
-                Ok(())
-            })
-        })
+            map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref())
+                .unwrap();
+            assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee");
+        });
     }
 
     #[test]
-    fn test_overlapping_folds() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_overlapping_folds() {
+        App::test((), |app| {
             let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
-            app.read(|app| {
-                let mut map = FoldMap::new(buffer.clone(), app);
-                map.fold(
-                    vec![
-                        Point::new(0, 2)..Point::new(2, 2),
-                        Point::new(0, 4)..Point::new(1, 0),
-                        Point::new(1, 2)..Point::new(3, 2),
-                        Point::new(3, 1)..Point::new(4, 1),
-                    ],
-                    app,
-                )?;
-                assert_eq!(map.text(app), "aa…eeeee");
-                Ok(())
-            })
+            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+            map.fold(
+                vec![
+                    Point::new(0, 2)..Point::new(2, 2),
+                    Point::new(0, 4)..Point::new(1, 0),
+                    Point::new(1, 2)..Point::new(3, 2),
+                    Point::new(3, 1)..Point::new(4, 1),
+                ],
+                app.as_ref(),
+            )
+            .unwrap();
+            assert_eq!(map.text(app.as_ref()), "aa…eeeee");
         })
     }
 
     #[test]
-    fn test_merging_folds_via_edit() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_merging_folds_via_edit() {
+        App::test((), |app| {
             let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
-            let mut map = app.read(|app| FoldMap::new(buffer.clone(), app));
-
-            app.read(|app| {
-                map.fold(
-                    vec![
-                        Point::new(0, 2)..Point::new(2, 2),
-                        Point::new(3, 1)..Point::new(4, 1),
-                    ],
-                    app,
-                )?;
-                assert_eq!(map.text(app), "aa…cccc\nd…eeeee");
-                Ok::<(), anyhow::Error>(())
-            })?;
-
-            let edits = buffer.update(&mut app, |buffer, ctx| {
+            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+
+            map.fold(
+                vec![
+                    Point::new(0, 2)..Point::new(2, 2),
+                    Point::new(3, 1)..Point::new(4, 1),
+                ],
+                app.as_ref(),
+            )
+            .unwrap();
+            assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee");
+
+            let edits = buffer.update(app, |buffer, ctx| {
                 let start_version = buffer.version.clone();
-                buffer.edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))?;
-                Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::<Vec<_>>())
-            })?;
-
-            app.read(|app| {
-                map.apply_edits(&edits, app)?;
-                assert_eq!(map.text(app), "aa…eeeee");
-                Ok(())
-            })
-        })
+                buffer
+                    .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))
+                    .unwrap();
+                buffer.edits_since(start_version).collect::<Vec<_>>()
+            });
+
+            map.apply_edits(&edits, app.as_ref()).unwrap();
+            assert_eq!(map.text(app.as_ref()), "aa…eeeee");
+        });
     }
 
     #[test]
-    fn test_random_folds() -> Result<()> {
+    fn test_random_folds() {
         use crate::editor::ToPoint;
         use crate::util::RandomCharIter;
         use rand::prelude::*;
@@ -597,15 +588,15 @@ mod tests {
             println!("{:?}", seed);
             let mut rng = StdRng::seed_from_u64(seed);
 
-            App::test((), |mut app| async move {
+            App::test((), |app| {
                 let buffer = app.add_model(|_| {
                     let len = rng.gen_range(0..10);
                     let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
                     Buffer::new(0, text)
                 });
-                let mut map = app.read(|app| FoldMap::new(buffer.clone(), app));
+                let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 
-                app.read(|app| {
+                {
                     let buffer = buffer.as_ref(app);
 
                     let fold_count = rng.gen_range(0..10);
@@ -616,93 +607,83 @@ mod tests {
                         fold_ranges.push(start..end);
                     }
 
-                    map.fold(fold_ranges, app)?;
+                    map.fold(fold_ranges, app.as_ref()).unwrap();
 
                     let mut expected_text = buffer.text();
-                    for fold_range in map.merged_fold_ranges(app).into_iter().rev() {
+                    for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
                         expected_text.replace_range(fold_range.start..fold_range.end, "…");
                     }
 
-                    assert_eq!(map.text(app), expected_text);
+                    assert_eq!(map.text(app.as_ref()), expected_text);
 
-                    for fold_range in map.merged_fold_ranges(app) {
+                    for fold_range in map.merged_fold_ranges(app.as_ref()) {
                         let display_point =
                             map.to_display_point(fold_range.start.to_point(buffer).unwrap());
                         assert!(map.is_line_folded(display_point.row()));
                     }
+                }
 
-                    Ok::<(), anyhow::Error>(())
-                })?;
-
-                let edits = buffer.update(&mut app, |buffer, ctx| {
+                let edits = buffer.update(app, |buffer, ctx| {
                     let start_version = buffer.version.clone();
                     let edit_count = rng.gen_range(1..10);
                     buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
-                    Ok::<_, anyhow::Error>(buffer.edits_since(start_version).collect::<Vec<_>>())
-                })?;
-
-                app.read(|app| {
-                    map.apply_edits(&edits, app)?;
-
-                    let buffer = map.buffer.as_ref(app);
-                    let mut expected_text = buffer.text();
-                    let mut expected_buffer_rows = Vec::new();
-                    let mut next_row = buffer.max_point().row;
-                    for fold_range in map.merged_fold_ranges(app).into_iter().rev() {
-                        let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
-                        let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
-                        expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
-                        next_row = fold_start.row;
+                    buffer.edits_since(start_version).collect::<Vec<_>>()
+                });
 
-                        expected_text.replace_range(fold_range.start..fold_range.end, "…");
-                    }
-                    expected_buffer_rows.extend((0..=next_row).rev());
-                    expected_buffer_rows.reverse();
+                map.apply_edits(&edits, app.as_ref()).unwrap();
 
-                    assert_eq!(map.text(app), expected_text);
+                let buffer = map.buffer.as_ref(app);
+                let mut expected_text = buffer.text();
+                let mut expected_buffer_rows = Vec::new();
+                let mut next_row = buffer.max_point().row;
+                for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
+                    let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
+                    let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
+                    expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
+                    next_row = fold_start.row;
 
-                    for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
-                        let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
-                        assert_eq!(
-                            map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
-                            expected_buffer_rows[idx..],
-                        );
-                    }
+                    expected_text.replace_range(fold_range.start..fold_range.end, "…");
+                }
+                expected_buffer_rows.extend((0..=next_row).rev());
+                expected_buffer_rows.reverse();
 
-                    Ok::<(), anyhow::Error>(())
-                })?;
+                assert_eq!(map.text(app.as_ref()), expected_text);
 
-                Ok::<(), anyhow::Error>(())
-            })?;
+                for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
+                    let display_row = map.to_display_point(Point::new(*buffer_row, 0)).row();
+                    assert_eq!(
+                        map.buffer_rows(display_row).unwrap().collect::<Vec<_>>(),
+                        expected_buffer_rows[idx..],
+                    );
+                }
+            });
         }
-
-        Ok(())
     }
 
     #[test]
-    fn test_buffer_rows() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_buffer_rows() {
+        App::test((), |app| {
             let text = sample_text(6, 6) + "\n";
             let buffer = app.add_model(|_| Buffer::new(0, text));
 
-            app.read(|app| {
-                let mut map = FoldMap::new(buffer.clone(), app);
-
-                map.fold(
-                    vec![
-                        Point::new(0, 2)..Point::new(2, 2),
-                        Point::new(3, 1)..Point::new(4, 1),
-                    ],
-                    app,
-                )?;
-
-                assert_eq!(map.text(app), "aa…cccc\nd…eeeee\nffffff\n");
-                assert_eq!(map.buffer_rows(0)?.collect::<Vec<_>>(), vec![0, 3, 5, 6]);
-                assert_eq!(map.buffer_rows(3)?.collect::<Vec<_>>(), vec![6]);
-
-                Ok(())
-            })
-        })
+            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
+
+            map.fold(
+                vec![
+                    Point::new(0, 2)..Point::new(2, 2),
+                    Point::new(3, 1)..Point::new(4, 1),
+                ],
+                app.as_ref(),
+            )
+            .unwrap();
+
+            assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n");
+            assert_eq!(
+                map.buffer_rows(0).unwrap().collect::<Vec<_>>(),
+                vec![0, 3, 5, 6]
+            );
+            assert_eq!(map.buffer_rows(3).unwrap().collect::<Vec<_>>(), vec![6]);
+        });
     }
 
     impl FoldMap {

zed/src/editor/display_map/mod.rs 🔗

@@ -292,52 +292,51 @@ pub fn collapse_tabs(
 mod tests {
     use super::*;
     use crate::test::*;
-    use anyhow::Error;
     use gpui::App;
 
     #[test]
-    fn test_chars_at() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_chars_at() {
+        App::test((), |app| {
             let text = sample_text(6, 6);
             let buffer = app.add_model(|_| Buffer::new(0, text));
             let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx));
-            buffer.update(&mut app, |buffer, ctx| {
-                buffer.edit(
-                    vec![
-                        Point::new(1, 0)..Point::new(1, 0),
-                        Point::new(1, 1)..Point::new(1, 1),
-                        Point::new(2, 1)..Point::new(2, 1),
-                    ],
-                    "\t",
-                    Some(ctx),
-                )
-            })?;
-
-            map.read(&app, |map, ctx| {
-                assert_eq!(
-                    map.chars_at(DisplayPoint::new(1, 0), ctx)?
-                        .take(10)
-                        .collect::<String>(),
-                    "    b   bb"
-                );
-                assert_eq!(
-                    map.chars_at(DisplayPoint::new(1, 2), ctx)?
-                        .take(10)
-                        .collect::<String>(),
-                    "  b   bbbb"
-                );
-                assert_eq!(
-                    map.chars_at(DisplayPoint::new(1, 6), ctx)?
-                        .take(13)
-                        .collect::<String>(),
-                    "  bbbbb\nc   c"
-                );
-
-                Ok::<(), Error>(())
-            })?;
-
-            Ok(())
-        })
+            buffer
+                .update(app, |buffer, ctx| {
+                    buffer.edit(
+                        vec![
+                            Point::new(1, 0)..Point::new(1, 0),
+                            Point::new(1, 1)..Point::new(1, 1),
+                            Point::new(2, 1)..Point::new(2, 1),
+                        ],
+                        "\t",
+                        Some(ctx),
+                    )
+                })
+                .unwrap();
+
+            let map = map.as_ref(app);
+            assert_eq!(
+                map.chars_at(DisplayPoint::new(1, 0), app.as_ref())
+                    .unwrap()
+                    .take(10)
+                    .collect::<String>(),
+                "    b   bb"
+            );
+            assert_eq!(
+                map.chars_at(DisplayPoint::new(1, 2), app.as_ref())
+                    .unwrap()
+                    .take(10)
+                    .collect::<String>(),
+                "  b   bbbb"
+            );
+            assert_eq!(
+                map.chars_at(DisplayPoint::new(1, 6), app.as_ref())
+                    .unwrap()
+                    .take(13)
+                    .collect::<String>(),
+                "  bbbbb\nc   c"
+            );
+        });
     }
 
     #[test]
@@ -364,14 +363,14 @@ mod tests {
     }
 
     #[test]
-    fn test_max_point() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_max_point() {
+        App::test((), |app| {
             let buffer = app.add_model(|_| Buffer::new(0, "aaa\n\t\tbbb"));
             let map = app.add_model(|ctx| DisplayMap::new(buffer.clone(), 4, ctx));
-            map.read(&app, |map, app| {
-                assert_eq!(map.max_point(app), DisplayPoint::new(1, 11))
-            });
-            Ok(())
-        })
+            assert_eq!(
+                map.as_ref(app).max_point(app.as_ref()),
+                DisplayPoint::new(1, 11)
+            )
+        });
     }
 }

zed/src/file_finder.rs 🔗

@@ -11,7 +11,7 @@ use gpui::{
     fonts::{Properties, Weight},
     geometry::vector::vec2f,
     keymap::{self, Binding},
-    App, AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext,
+    AppContext, Axis, Border, Entity, ModelHandle, MutableAppContext, View, ViewContext,
     ViewHandle, WeakViewHandle,
 };
 use std::cmp;
@@ -394,20 +394,23 @@ mod tests {
         editor, settings,
         workspace::{Workspace, WorkspaceView},
     };
-    use anyhow::Result;
     use gpui::App;
     use smol::fs;
     use tempdir::TempDir;
 
     #[test]
-    fn test_matching_paths() -> Result<()> {
-        App::test((), |mut app| async move {
-            let tmp_dir = TempDir::new("example")?;
-            fs::create_dir(tmp_dir.path().join("a")).await?;
-            fs::write(tmp_dir.path().join("a/banana"), "banana").await?;
-            fs::write(tmp_dir.path().join("a/bandana"), "bandana").await?;
-            super::init(&mut app);
-            editor::init(&mut app);
+    fn test_matching_paths() {
+        App::test_async((), |app| async move {
+            let tmp_dir = TempDir::new("example").unwrap();
+            fs::create_dir(tmp_dir.path().join("a")).await.unwrap();
+            fs::write(tmp_dir.path().join("a/banana"), "banana")
+                .await
+                .unwrap();
+            fs::write(tmp_dir.path().join("a/bandana"), "bandana")
+                .await
+                .unwrap();
+            super::init(app);
+            editor::init(app);
 
             let settings = settings::channel(&app.font_cache()).unwrap().1;
             let workspace = app.add_model(|ctx| Workspace::new(vec![tmp_dir.path().into()], ctx));
@@ -420,16 +423,15 @@ mod tests {
                 "file_finder:toggle".into(),
                 (),
             );
-            let (finder, query_buffer) = workspace_view.read(&app, |view, ctx| {
-                let finder = view
-                    .modal()
-                    .cloned()
-                    .unwrap()
-                    .downcast::<FileFinder>()
-                    .unwrap();
-                let query_buffer = finder.as_ref(ctx).query_buffer.clone();
-                (finder, query_buffer)
-            });
+
+            let finder = workspace_view
+                .as_ref(app)
+                .modal()
+                .cloned()
+                .unwrap()
+                .downcast::<FileFinder>()
+                .unwrap();
+            let query_buffer = finder.as_ref(app).query_buffer.clone();
 
             let chain = vec![finder.id(), query_buffer.id()];
             app.dispatch_action(window_id, chain.clone(), "buffer:insert", "b".to_string());
@@ -452,7 +454,7 @@ mod tests {
             //     (),
             // );
             // app.finish_pending_tasks().await; // Load Buffer and open BufferView.
-            // let active_pane = workspace_view.read(&app, |view, _| view.active_pane().clone());
+            // let active_pane = workspace_view.as_ref(app).active_pane().clone();
             // assert_eq!(
             //     active_pane.state(&app),
             //     pane::State {
@@ -462,7 +464,6 @@ mod tests {
             //         }]
             //     }
             // );
-            Ok(())
-        })
+        });
     }
 }

zed/src/main.rs 🔗

@@ -1,5 +1,5 @@
 use fs::OpenOptions;
-use gpui::platform::{current as platform, PathPromptOptions, Runner as _};
+use gpui::platform::PathPromptOptions;
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 use std::{fs, path::PathBuf};
@@ -13,6 +13,7 @@ fn main() {
 
     let app = gpui::App::new(assets::Assets).unwrap();
     let (_, settings_rx) = settings::channel(&app.font_cache()).unwrap();
+    app.set_menus(menus::MENUS);
     app.on_menu_command({
         let settings_rx = settings_rx.clone();
         move |command, ctx| match command {
@@ -34,7 +35,7 @@ fn main() {
             _ => ctx.dispatch_global_action(command, ()),
         }
     })
-    .run(move |ctx| {
+    .on_finish_launching(move |ctx| {
         workspace::init(ctx);
         editor::init(ctx);
         file_finder::init(ctx);
@@ -53,7 +54,8 @@ fn main() {
                 },
             );
         }
-    });
+    })
+    .run();
 }
 
 fn init_logger() {

zed/src/workspace/mod.rs 🔗

@@ -9,7 +9,7 @@ pub use workspace::*;
 pub use workspace_view::*;
 
 use crate::{settings::Settings, watch};
-use gpui::{App, MutableAppContext};
+use gpui::MutableAppContext;
 use std::path::PathBuf;
 
 pub fn init(app: &mut MutableAppContext) {
@@ -64,10 +64,10 @@ mod tests {
 
     #[test]
     fn test_open_paths_action() {
-        App::test((), |mut app| async move {
+        App::test((), |app| {
             let settings = settings::channel(&app.font_cache()).unwrap().1;
 
-            init(&mut app);
+            init(app);
 
             let dir = temp_tree(json!({
                 "a": {
@@ -94,7 +94,7 @@ mod tests {
                     settings: settings.clone(),
                 },
             );
-            assert_eq!(app.window_ids().len(), 1);
+            assert_eq!(app.window_ids().count(), 1);
 
             app.dispatch_global_action(
                 "workspace:open_paths",
@@ -103,11 +103,19 @@ mod tests {
                     settings: settings.clone(),
                 },
             );
-            assert_eq!(app.window_ids().len(), 1);
-            let workspace_view_1 = app.root_view::<WorkspaceView>(app.window_ids()[0]).unwrap();
-            workspace_view_1.read(&app, |view, app| {
-                assert_eq!(view.workspace.as_ref(app).worktrees().len(), 2);
-            });
+            assert_eq!(app.window_ids().count(), 1);
+            let workspace_view_1 = app
+                .root_view::<WorkspaceView>(app.window_ids().next().unwrap())
+                .unwrap();
+            assert_eq!(
+                workspace_view_1
+                    .as_ref(app)
+                    .workspace
+                    .as_ref(app)
+                    .worktrees()
+                    .len(),
+                2
+            );
 
             app.dispatch_global_action(
                 "workspace:open_paths",
@@ -119,7 +127,7 @@ mod tests {
                     settings: settings.clone(),
                 },
             );
-            assert_eq!(app.window_ids().len(), 2);
+            assert_eq!(app.window_ids().count(), 2);
         });
     }
 }

zed/src/workspace/pane.rs 🔗

@@ -5,7 +5,7 @@ use gpui::{
     elements::*,
     geometry::{rect::RectF, vector::vec2f},
     keymap::Binding,
-    App, AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext,
+    AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext,
 };
 use std::cmp;
 

zed/src/workspace/workspace.rs 🔗

@@ -200,23 +200,22 @@ impl Entity for Workspace {
 
 #[cfg(test)]
 pub trait WorkspaceHandle {
-    fn file_entries(&self, app: &gpui::App) -> Vec<(usize, usize)>;
+    fn file_entries(&self, app: &mut MutableAppContext) -> Vec<(usize, usize)>;
 }
 
 #[cfg(test)]
 impl WorkspaceHandle for ModelHandle<Workspace> {
-    fn file_entries(&self, app: &gpui::App) -> Vec<(usize, usize)> {
-        self.read(&app, |w, app| {
-            w.worktrees()
-                .iter()
-                .flat_map(|tree| {
-                    let tree_id = tree.id();
-                    tree.as_ref(app)
-                        .files()
-                        .map(move |file| (tree_id, file.entry_id))
-                })
-                .collect::<Vec<_>>()
-        })
+    fn file_entries(&self, app: &mut MutableAppContext) -> Vec<(usize, usize)> {
+        self.as_ref(app)
+            .worktrees()
+            .iter()
+            .flat_map(|tree| {
+                let tree_id = tree.id();
+                tree.as_ref(app)
+                    .files()
+                    .map(move |file| (tree_id, file.entry_id))
+            })
+            .collect::<Vec<_>>()
     }
 }
 
@@ -228,8 +227,8 @@ mod tests {
     use serde_json::json;
 
     #[test]
-    fn test_open_entry() -> Result<(), Arc<anyhow::Error>> {
-        App::test((), |mut app| async move {
+    fn test_open_entry() {
+        App::test_async((), |app| async move {
             let dir = temp_tree(json!({
                 "a": {
                     "aa": "aa contents",
@@ -241,32 +240,29 @@ mod tests {
             app.finish_pending_tasks().await; // Open and populate worktree.
 
             // Get the first file entry.
-            let entry = workspace.read(&app, |w, app| {
-                let tree = w.worktrees.iter().next().unwrap();
-                let entry_id = tree.as_ref(app).files().next().unwrap().entry_id;
-                (tree.id(), entry_id)
-            });
+            let tree = workspace.as_ref(app).worktrees.iter().next().unwrap();
+            let entry_id = tree.as_ref(app).files().next().unwrap().entry_id;
+            let entry = (tree.id(), entry_id);
 
             // Open the same entry twice before it finishes loading.
-            let (future_1, future_2) = workspace.update(&mut app, |w, app| {
+            let (future_1, future_2) = workspace.update(app, |w, app| {
                 (
                     w.open_entry(entry, app).unwrap(),
                     w.open_entry(entry, app).unwrap(),
                 )
             });
 
-            let handle_1 = future_1.await?;
-            let handle_2 = future_2.await?;
+            let handle_1 = future_1.await.unwrap();
+            let handle_2 = future_2.await.unwrap();
             assert_eq!(handle_1.id(), handle_2.id());
 
             // Open the same entry again now that it has loaded
             let handle_3 = workspace
-                .update(&mut app, |w, app| w.open_entry(entry, app).unwrap())
-                .await?;
+                .update(app, |w, app| w.open_entry(entry, app).unwrap())
+                .await
+                .unwrap();
 
             assert_eq!(handle_3.id(), handle_1.id());
-
-            Ok(())
         })
     }
 }

zed/src/workspace/workspace_view.rs 🔗

@@ -2,8 +2,8 @@ use super::{pane, Pane, PaneGroup, SplitDirection, Workspace};
 use crate::{settings::Settings, watch};
 use futures_core::future::LocalBoxFuture;
 use gpui::{
-    color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, App,
-    AppContext, Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle,
+    color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext,
+    Entity, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle,
 };
 use log::{error, info};
 use std::{collections::HashSet, path::PathBuf};
@@ -389,13 +389,12 @@ impl View for WorkspaceView {
 mod tests {
     use super::{pane, Workspace, WorkspaceView};
     use crate::{settings, test::temp_tree, workspace::WorkspaceHandle as _};
-    use anyhow::Result;
     use gpui::App;
     use serde_json::json;
 
     #[test]
-    fn test_open_entry() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_open_entry() {
+        App::test_async((), |app| async move {
             let dir = temp_tree(json!({
                 "a": {
                     "aa": "aa contents",
@@ -407,64 +406,70 @@ mod tests {
             let settings = settings::channel(&app.font_cache()).unwrap().1;
             let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx));
             app.finish_pending_tasks().await; // Open and populate worktree.
-            let entries = workspace.file_entries(&app);
+            let entries = workspace.file_entries(app);
 
             let (_, workspace_view) =
                 app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx));
 
             // Open the first entry
-            workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
+            workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx));
             app.finish_pending_tasks().await;
 
-            workspace_view.read(&app, |w, app| {
-                assert_eq!(w.active_pane().as_ref(app).items().len(), 1);
-            });
+            assert_eq!(
+                workspace_view
+                    .as_ref(app)
+                    .active_pane()
+                    .as_ref(app)
+                    .items()
+                    .len(),
+                1
+            );
 
             // Open the second entry
-            workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[1], ctx));
+            workspace_view.update(app, |w, ctx| w.open_entry(entries[1], ctx));
             app.finish_pending_tasks().await;
 
-            workspace_view.read(&app, |w, app| {
-                let active_pane = w.active_pane().as_ref(app);
-                assert_eq!(active_pane.items().len(), 2);
-                assert_eq!(
-                    active_pane.active_item().unwrap().entry_id(app),
-                    Some(entries[1])
-                );
-            });
+            let active_pane = workspace_view.as_ref(app).active_pane().as_ref(app);
+            assert_eq!(active_pane.items().len(), 2);
+            assert_eq!(
+                active_pane.active_item().unwrap().entry_id(app.as_ref()),
+                Some(entries[1])
+            );
 
             // Open the first entry again
-            workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
+            workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx));
             app.finish_pending_tasks().await;
 
-            workspace_view.read(&app, |w, app| {
-                let active_pane = w.active_pane().as_ref(app);
-                assert_eq!(active_pane.items().len(), 2);
-                assert_eq!(
-                    active_pane.active_item().unwrap().entry_id(app),
-                    Some(entries[0])
-                );
-            });
+            let active_pane = workspace_view.as_ref(app).active_pane().as_ref(app);
+            assert_eq!(active_pane.items().len(), 2);
+            assert_eq!(
+                active_pane.active_item().unwrap().entry_id(app.as_ref()),
+                Some(entries[0])
+            );
 
             // Open the third entry twice concurrently
-            workspace_view.update(&mut app, |w, ctx| {
+            workspace_view.update(app, |w, ctx| {
                 w.open_entry(entries[2], ctx);
                 w.open_entry(entries[2], ctx);
             });
             app.finish_pending_tasks().await;
 
-            workspace_view.read(&app, |w, app| {
-                assert_eq!(w.active_pane().as_ref(app).items().len(), 3);
-            });
-
-            Ok(())
-        })
+            assert_eq!(
+                workspace_view
+                    .as_ref(app)
+                    .active_pane()
+                    .as_ref(app)
+                    .items()
+                    .len(),
+                3
+            );
+        });
     }
 
     #[test]
-    fn test_pane_actions() -> Result<()> {
-        App::test((), |mut app| async move {
-            pane::init(&mut app);
+    fn test_pane_actions() {
+        App::test_async((), |app| async move {
+            pane::init(app);
 
             let dir = temp_tree(json!({
                 "a": {
@@ -474,35 +479,37 @@ mod tests {
                 },
             }));
 
-            let settings = settings::channel(&app.font_cache()).unwrap().1;
+            let settings = settings::channel(app.font_cache()).unwrap().1;
             let workspace = app.add_model(|ctx| Workspace::new(vec![dir.path().into()], ctx));
             app.finish_pending_tasks().await; // Open and populate worktree.
-            let entries = workspace.file_entries(&app);
+            let entries = workspace.file_entries(app);
 
             let (window_id, workspace_view) =
                 app.add_window(|ctx| WorkspaceView::new(workspace.clone(), settings, ctx));
 
-            workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
+            workspace_view.update(app, |w, ctx| w.open_entry(entries[0], ctx));
             app.finish_pending_tasks().await;
 
-            let pane_1 = workspace_view.read(&app, |w, _| w.active_pane().clone());
+            let pane_1 = workspace_view.as_ref(app).active_pane().clone();
 
             app.dispatch_action(window_id, vec![pane_1.id()], "pane:split_right", ());
-            let pane_2 = workspace_view.read(&app, |w, _| w.active_pane().clone());
+            let pane_2 = workspace_view.as_ref(app).active_pane().clone();
             assert_ne!(pane_1, pane_2);
 
-            pane_2.read(&app, |p, app| {
-                assert_eq!(p.active_item().unwrap().entry_id(app), Some(entries[0]));
-            });
+            assert_eq!(
+                pane_2
+                    .as_ref(app)
+                    .active_item()
+                    .unwrap()
+                    .entry_id(app.downgrade()),
+                Some(entries[0])
+            );
 
             app.dispatch_action(window_id, vec![pane_2.id()], "pane:close_active_item", ());
 
-            workspace_view.read(&app, |w, _| {
-                assert_eq!(w.panes.len(), 1);
-                assert_eq!(w.active_pane(), &pane_1)
-            });
-
-            Ok(())
-        })
+            let w = workspace_view.as_ref(app);
+            assert_eq!(w.panes.len(), 1);
+            assert_eq!(w.active_pane(), &pane_1);
+        });
     }
 }

zed/src/worktree/worktree.rs 🔗

@@ -648,8 +648,8 @@ mod test {
     use std::os::unix;
 
     #[test]
-    fn test_populate_and_search() -> Result<()> {
-        App::test((), |mut app| async move {
+    fn test_populate_and_search() {
+        App::test_async((), |app| async move {
             let dir = temp_tree(json!({
                 "root": {
                     "apple": "",
@@ -666,33 +666,31 @@ mod test {
             }));
 
             let root_link_path = dir.path().join("root_link");
-            unix::fs::symlink(&dir.path().join("root"), &root_link_path)?;
+            unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();
 
             let tree = app.add_model(|ctx| Worktree::new(1, root_link_path, Some(ctx)));
             app.finish_pending_tasks().await;
 
-            tree.read(&app, |tree, _| {
-                assert_eq!(tree.file_count(), 4);
-                let results = match_paths(&[tree.clone()], "bna", false, false, 10)
-                    .iter()
-                    .map(|result| tree.entry_path(result.entry_id))
-                    .collect::<Result<Vec<PathBuf>, _>>()
-                    .unwrap();
-                assert_eq!(
-                    results,
-                    vec![
-                        PathBuf::from("root_link/banana/carrot/date"),
-                        PathBuf::from("root_link/banana/carrot/endive"),
-                    ]
-                );
-            });
-            Ok(())
-        })
+            let tree = tree.as_ref(app);
+            assert_eq!(tree.file_count(), 4);
+            let results = match_paths(&[tree.clone()], "bna", false, false, 10)
+                .iter()
+                .map(|result| tree.entry_path(result.entry_id))
+                .collect::<Result<Vec<PathBuf>, _>>()
+                .unwrap();
+            assert_eq!(
+                results,
+                vec![
+                    PathBuf::from("root_link/banana/carrot/date"),
+                    PathBuf::from("root_link/banana/carrot/endive"),
+                ]
+            );
+        });
     }
 
     #[test]
     fn test_save_file() {
-        App::test((), |mut app| async move {
+        App::test_async((), |app| async move {
             let dir = temp_tree(json!({
                 "file1": "the old contents",
             }));
@@ -700,24 +698,18 @@ mod test {
             let tree = app.add_model(|ctx| Worktree::new(1, dir.path(), Some(ctx)));
             app.finish_pending_tasks().await;
 
-            let file_id = tree.read(&app, |tree, _| {
-                let entry = tree.files().next().unwrap();
-                assert_eq!(entry.path.file_name().unwrap(), "file1");
-                entry.entry_id
-            });
+            let entry = tree.as_ref(app).files().next().unwrap();
+            assert_eq!(entry.path.file_name().unwrap(), "file1");
+            let file_id = entry.entry_id;
 
             let buffer = Buffer::new(1, "a line of text.\n".repeat(10 * 1024));
 
-            tree.update(&mut app, |tree, ctx| {
+            tree.update(app, |tree, ctx| {
                 smol::block_on(tree.save(file_id, buffer.snapshot(), ctx.app())).unwrap()
             });
 
-            let history = tree
-                .read(&app, |tree, _| tree.load_history(file_id))
-                .await
-                .unwrap();
-
+            let history = tree.as_ref(app).load_history(file_id).await.unwrap();
             assert_eq!(history.base_text, buffer.text());
-        })
+        });
     }
 }