Merge pull request #40 from zed-industries/close-window

Nathan Sobo created

Correctly handle closing windows and removing entities

Change summary

gpui/src/app.rs                   | 522 ++++++++++++++++++--------------
gpui/src/platform/mac/platform.rs |  13 
gpui/src/platform/mac/window.rs   |  28 +
gpui/src/platform/mod.rs          |   1 
gpui/src/platform/test.rs         |   6 
gpui/src/presenter.rs             |  13 
zed/src/editor/buffer_element.rs  |  34 +
zed/src/editor/buffer_view.rs     |  33 -
zed/src/file_finder.rs            |   9 
zed/src/lib.rs                    |   1 
zed/src/menus.rs                  |   5 
zed/src/settings.rs               |   4 
zed/src/watch.rs                  |  65 ----
zed/src/workspace.rs              |   6 
zed/src/workspace/pane.rs         |   5 
15 files changed, 388 insertions(+), 357 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -392,7 +392,6 @@ pub struct MutableAppContext {
     subscriptions: HashMap<usize, Vec<Subscription>>,
     model_observations: HashMap<usize, Vec<ModelObservation>>,
     view_observations: HashMap<usize, Vec<ViewObservation>>,
-    window_invalidations: HashMap<usize, WindowInvalidation>,
     presenters_and_platform_windows:
         HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
     debug_elements_callbacks: HashMap<usize, Box<dyn Fn(&AppContext) -> crate::json::Value>>,
@@ -417,6 +416,7 @@ impl MutableAppContext {
             assets: Arc::new(AssetCache::new(asset_source)),
             ctx: AppContext {
                 models: Default::default(),
+                views: Default::default(),
                 windows: Default::default(),
                 values: Default::default(),
                 ref_counts: Arc::new(Mutex::new(RefCounts::default())),
@@ -433,7 +433,6 @@ impl MutableAppContext {
             subscriptions: HashMap::new(),
             model_observations: HashMap::new(),
             view_observations: HashMap::new(),
-            window_invalidations: HashMap::new(),
             presenters_and_platform_windows: HashMap::new(),
             debug_elements_callbacks: HashMap::new(),
             foreground,
@@ -549,7 +548,7 @@ impl MutableAppContext {
         self.ctx
             .windows
             .get(&window_id)
-            .and_then(|window| window.root_view.as_ref().unwrap().clone().downcast::<T>())
+            .and_then(|window| window.root_view.clone().downcast::<T>())
     }
 
     pub fn root_view_id(&self, window_id: usize) -> Option<usize> {
@@ -564,7 +563,7 @@ impl MutableAppContext {
         self.ctx.render_view(window_id, view_id)
     }
 
-    pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, ElementBox>> {
+    pub fn render_views(&self, window_id: usize) -> HashMap<usize, ElementBox> {
         self.ctx.render_views(window_id)
     }
 
@@ -637,12 +636,7 @@ impl MutableAppContext {
         let mut halted_dispatch = false;
 
         for view_id in path.iter().rev() {
-            if let Some(mut view) = self
-                .ctx
-                .windows
-                .get_mut(&window_id)
-                .and_then(|w| w.views.remove(view_id))
-            {
+            if let Some(mut view) = self.ctx.views.remove(&(window_id, *view_id)) {
                 let type_id = view.as_any().type_id();
 
                 if let Some((name, mut handlers)) = self
@@ -663,12 +657,7 @@ impl MutableAppContext {
                         .insert(name, handlers);
                 }
 
-                self.ctx
-                    .windows
-                    .get_mut(&window_id)
-                    .unwrap()
-                    .views
-                    .insert(*view_id, view);
+                self.ctx.views.insert((window_id, *view_id), view);
 
                 if halted_dispatch {
                     break;
@@ -712,12 +701,7 @@ impl MutableAppContext {
         let mut context_chain = Vec::new();
         let mut context = keymap::Context::default();
         for view_id in &responder_chain {
-            if let Some(view) = self
-                .ctx
-                .windows
-                .get(&window_id)
-                .and_then(|w| w.views.get(view_id))
-            {
+            if let Some(view) = self.ctx.views.get(&(window_id, *view_id)) {
                 context.extend(view.keymap_context(self.as_ref()));
                 context_chain.push(context.clone());
             } else {
@@ -759,11 +743,12 @@ impl MutableAppContext {
     {
         self.pending_flushes += 1;
         let model_id = post_inc(&mut self.next_entity_id);
+        let handle = ModelHandle::new(model_id, &self.ctx.ref_counts);
         let mut ctx = ModelContext::new(self, model_id);
         let model = build_model(&mut ctx);
         self.ctx.models.insert(model_id, Box::new(model));
         self.flush_effects();
-        ModelHandle::new(model_id, &self.ctx.ref_counts)
+        handle
     }
 
     pub fn add_window<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>)
@@ -773,14 +758,27 @@ impl MutableAppContext {
     {
         self.pending_flushes += 1;
         let window_id = post_inc(&mut self.next_window_id);
-        self.ctx.windows.insert(window_id, Window::default());
+        let root_view = self.add_view(window_id, build_root_view);
+
+        self.ctx.windows.insert(
+            window_id,
+            Window {
+                root_view: root_view.clone().into(),
+                focused_view_id: root_view.id(),
+                invalidation: None,
+            },
+        );
         self.open_platform_window(window_id);
-        let root_handle = self.add_view(window_id, build_root_view);
-        self.ctx.windows.get_mut(&window_id).unwrap().root_view = Some(root_handle.clone().into());
-        self.focus(window_id, root_handle.id());
+        root_view.update(self, |view, ctx| view.on_focus(ctx));
         self.flush_effects();
 
-        (window_id, root_handle)
+        (window_id, root_view)
+    }
+
+    pub fn remove_window(&mut self, window_id: usize) {
+        self.ctx.windows.remove(&window_id);
+        self.presenters_and_platform_windows.remove(&window_id);
+        self.remove_dropped_entities();
     }
 
     fn open_platform_window(&mut self, window_id: usize) {
@@ -839,6 +837,13 @@ impl MutableAppContext {
             }));
         }
 
+        {
+            let mut app = self.upgrade();
+            window.on_close(Box::new(move || {
+                app.update(|ctx| ctx.remove_window(window_id));
+            }));
+        }
+
         self.presenters_and_platform_windows
             .insert(window_id, (presenter.clone(), window));
 
@@ -867,19 +872,18 @@ impl MutableAppContext {
     {
         let view_id = post_inc(&mut self.next_entity_id);
         self.pending_flushes += 1;
+        let handle = ViewHandle::new(window_id, view_id, &self.ctx.ref_counts);
         let mut ctx = ViewContext::new(self, window_id, view_id);
         let handle = if let Some(view) = build_view(&mut ctx) {
+            self.ctx.views.insert((window_id, view_id), Box::new(view));
             if let Some(window) = self.ctx.windows.get_mut(&window_id) {
-                window.views.insert(view_id, Box::new(view));
-            } else {
-                panic!("Window does not exist");
+                window
+                    .invalidation
+                    .get_or_insert_with(Default::default)
+                    .updated
+                    .insert(view_id);
             }
-            self.window_invalidations
-                .entry(window_id)
-                .or_default()
-                .updated
-                .insert(view_id);
-            Some(ViewHandle::new(window_id, view_id, &self.ctx.ref_counts))
+            Some(handle)
         } else {
             None
         };
@@ -904,13 +908,22 @@ impl MutableAppContext {
             for (window_id, view_id) in dropped_views {
                 self.subscriptions.remove(&view_id);
                 self.model_observations.remove(&view_id);
-                if let Some(window) = self.ctx.windows.get_mut(&window_id) {
-                    self.window_invalidations
-                        .entry(window_id)
-                        .or_default()
+                self.ctx.views.remove(&(window_id, view_id));
+                let change_focus_to = self.ctx.windows.get_mut(&window_id).and_then(|window| {
+                    window
+                        .invalidation
+                        .get_or_insert_with(Default::default)
                         .removed
                         .push(view_id);
-                    window.views.remove(&view_id);
+                    if window.focused_view_id == view_id {
+                        Some(window.root_view.id())
+                    } else {
+                        None
+                    }
+                });
+
+                if let Some(view_id) = change_focus_to {
+                    self.focus(window_id, view_id);
                 }
             }
 
@@ -956,7 +969,11 @@ impl MutableAppContext {
 
     fn update_windows(&mut self) {
         let mut invalidations = HashMap::new();
-        std::mem::swap(&mut invalidations, &mut self.window_invalidations);
+        for (window_id, window) in &mut self.ctx.windows {
+            if let Some(invalidation) = window.invalidation.take() {
+                invalidations.insert(*window_id, invalidation);
+            }
+        }
 
         for (window_id, invalidation) in invalidations {
             if let Some((presenter, mut window)) =
@@ -992,12 +1009,7 @@ impl MutableAppContext {
                         view_id,
                         callback,
                     } => {
-                        if let Some(mut view) = self
-                            .ctx
-                            .windows
-                            .get_mut(&window_id)
-                            .and_then(|window| window.views.remove(view_id))
-                        {
+                        if let Some(mut view) = self.ctx.views.remove(&(*window_id, *view_id)) {
                             callback(
                                 view.as_any_mut(),
                                 payload.as_ref(),
@@ -1005,12 +1017,7 @@ impl MutableAppContext {
                                 *window_id,
                                 *view_id,
                             );
-                            self.ctx
-                                .windows
-                                .get_mut(&window_id)
-                                .unwrap()
-                                .views
-                                .insert(*view_id, view);
+                            self.ctx.views.insert((*window_id, *view_id), view);
                             true
                         } else {
                             false
@@ -1047,12 +1054,7 @@ impl MutableAppContext {
                             view_id,
                             callback,
                         } => {
-                            if let Some(mut view) = self
-                                .ctx
-                                .windows
-                                .get_mut(window_id)
-                                .and_then(|w| w.views.remove(view_id))
-                            {
+                            if let Some(mut view) = self.ctx.views.remove(&(*window_id, *view_id)) {
                                 callback(
                                     view.as_any_mut(),
                                     observed_id,
@@ -1060,12 +1062,7 @@ impl MutableAppContext {
                                     *window_id,
                                     *view_id,
                                 );
-                                self.ctx
-                                    .windows
-                                    .get_mut(window_id)
-                                    .unwrap()
-                                    .views
-                                    .insert(*view_id, view);
+                                self.ctx.views.insert((*window_id, *view_id), view);
                                 true
                             } else {
                                 false
@@ -1085,25 +1082,21 @@ impl MutableAppContext {
     }
 
     fn notify_view_observers(&mut self, window_id: usize, view_id: usize) {
-        self.window_invalidations
-            .entry(window_id)
-            .or_default()
-            .updated
-            .insert(view_id);
+        if let Some(window) = self.ctx.windows.get_mut(&window_id) {
+            window
+                .invalidation
+                .get_or_insert_with(Default::default)
+                .updated
+                .insert(view_id);
+        }
 
         if let Some(observations) = self.view_observations.remove(&view_id) {
-            if self
-                .ctx
-                .windows
-                .get(&window_id)
-                .map_or(false, |w| w.views.contains_key(&view_id))
-            {
+            if self.ctx.views.contains_key(&(window_id, view_id)) {
                 for mut observation in observations {
                     let alive = if let Some(mut view) = self
                         .ctx
-                        .windows
-                        .get_mut(&observation.window_id)
-                        .and_then(|w| w.views.remove(&observation.view_id))
+                        .views
+                        .remove(&(observation.window_id, observation.view_id))
                     {
                         (observation.callback)(
                             view.as_any_mut(),
@@ -1114,11 +1107,8 @@ impl MutableAppContext {
                             observation.view_id,
                         );
                         self.ctx
-                            .windows
-                            .get_mut(&observation.window_id)
-                            .unwrap()
                             .views
-                            .insert(observation.view_id, view);
+                            .insert((observation.window_id, observation.view_id), view);
                         true
                     } else {
                         false
@@ -1140,7 +1130,7 @@ impl MutableAppContext {
             .ctx
             .windows
             .get(&window_id)
-            .and_then(|w| w.focused_view)
+            .map(|w| w.focused_view_id)
             .map_or(false, |cur_focused| cur_focused == focused_id)
         {
             return;
@@ -1148,41 +1138,28 @@ impl MutableAppContext {
 
         self.pending_flushes += 1;
 
-        if let Some((blurred_id, mut blurred)) =
-            self.ctx.windows.get_mut(&window_id).and_then(|w| {
-                let blurred_view = w.focused_view;
-                w.focused_view = Some(focused_id);
-                blurred_view.and_then(|id| w.views.remove(&id).map(|view| (id, view)))
-            })
-        {
-            blurred.on_blur(self, window_id, blurred_id);
-            self.ctx
-                .windows
-                .get_mut(&window_id)
-                .unwrap()
-                .views
-                .insert(blurred_id, blurred);
+        let blurred_id = self.ctx.windows.get_mut(&window_id).map(|window| {
+            let blurred_id = window.focused_view_id;
+            window.focused_view_id = focused_id;
+            blurred_id
+        });
+
+        if let Some(blurred_id) = blurred_id {
+            if let Some(mut blurred_view) = self.ctx.views.remove(&(window_id, blurred_id)) {
+                blurred_view.on_blur(self, window_id, blurred_id);
+                self.ctx.views.insert((window_id, blurred_id), blurred_view);
+            }
         }
 
-        if let Some(mut focused) = self
-            .ctx
-            .windows
-            .get_mut(&window_id)
-            .and_then(|w| w.views.remove(&focused_id))
-        {
-            focused.on_focus(self, window_id, focused_id);
-            self.ctx
-                .windows
-                .get_mut(&window_id)
-                .unwrap()
-                .views
-                .insert(focused_id, focused);
+        if let Some(mut focused_view) = self.ctx.views.remove(&(window_id, focused_id)) {
+            focused_view.on_focus(self, window_id, focused_id);
+            self.ctx.views.insert((window_id, focused_id), focused_view);
         }
 
         self.flush_effects();
     }
 
-    fn spawn<F, T>(&mut self, future: F) -> EntityTask<T>
+    fn spawn<F, T>(&mut self, spawner: Spawner, future: F) -> EntityTask<T>
     where
         F: 'static + Future,
         T: 'static,
@@ -1193,20 +1170,20 @@ impl MutableAppContext {
             let app = app.clone();
             self.foreground.spawn(async move {
                 let output = future.await;
-                *app.borrow_mut()
+                app.borrow_mut()
                     .handle_future_output(task_id, Box::new(output))
-                    .downcast::<T>()
-                    .unwrap()
+                    .map(|output| *output.downcast::<T>().unwrap())
             })
         };
         EntityTask::new(
             task_id,
             task,
+            spawner,
             TaskHandlerMap::Future(self.future_handlers.clone()),
         )
     }
 
-    fn spawn_stream<F, T>(&mut self, mut stream: F) -> EntityTask<T>
+    fn spawn_stream<F, T>(&mut self, spawner: Spawner, mut stream: F) -> EntityTask<T>
     where
         F: 'static + Stream + Unpin,
         T: 'static,
@@ -1228,20 +1205,24 @@ impl MutableAppContext {
                 }
             }
 
-            *app.borrow_mut()
+            app.borrow_mut()
                 .stream_completed(task_id)
-                .downcast::<T>()
-                .unwrap()
+                .map(|output| *output.downcast::<T>().unwrap())
         });
 
         EntityTask::new(
             task_id,
             task,
+            spawner,
             TaskHandlerMap::Stream(self.stream_handlers.clone()),
         )
     }
 
-    fn handle_future_output(&mut self, task_id: usize, output: Box<dyn Any>) -> Box<dyn Any> {
+    fn handle_future_output(
+        &mut self,
+        task_id: usize,
+        output: Box<dyn Any>,
+    ) -> Option<Box<dyn Any>> {
         self.pending_flushes += 1;
         let future_callback = self.future_handlers.borrow_mut().remove(&task_id).unwrap();
         let result = future_callback(output, self);
@@ -1260,7 +1241,7 @@ impl MutableAppContext {
         halt
     }
 
-    fn stream_completed(&mut self, task_id: usize) -> Box<dyn Any> {
+    fn stream_completed(&mut self, task_id: usize) -> Option<Box<dyn Any>> {
         self.pending_flushes += 1;
 
         let handler = self.stream_handlers.borrow_mut().remove(&task_id).unwrap();
@@ -1285,9 +1266,9 @@ impl ReadModel for MutableAppContext {
             model
                 .as_any()
                 .downcast_ref()
-                .expect("Downcast is type safe")
+                .expect("downcast is type safe")
         } else {
-            panic!("Circular model reference");
+            panic!("circular model reference");
         }
     }
 }
@@ -1305,28 +1286,24 @@ impl UpdateModel for MutableAppContext {
                 model
                     .as_any_mut()
                     .downcast_mut()
-                    .expect("Downcast is type safe"),
+                    .expect("downcast is type safe"),
                 &mut ctx,
             );
             self.ctx.models.insert(handle.model_id, model);
             self.flush_effects();
             result
         } else {
-            panic!("Circular model update");
+            panic!("circular model update");
         }
     }
 }
 
 impl ReadView for MutableAppContext {
     fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
-        if let Some(window) = self.ctx.windows.get(&handle.window_id) {
-            if let Some(view) = window.views.get(&handle.view_id) {
-                view.as_any().downcast_ref().expect("Downcast is type safe")
-            } else {
-                panic!("Circular view reference");
-            }
+        if let Some(view) = self.ctx.views.get(&(handle.window_id, handle.view_id)) {
+            view.as_any().downcast_ref().expect("downcast is type safe")
         } else {
-            panic!("Window does not exist");
+            panic!("circular view reference");
         }
     }
 }
@@ -1338,29 +1315,22 @@ impl UpdateView for MutableAppContext {
         F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
     {
         self.pending_flushes += 1;
-        let mut view = if let Some(window) = self.ctx.windows.get_mut(&handle.window_id) {
-            if let Some(view) = window.views.remove(&handle.view_id) {
-                view
-            } else {
-                panic!("Circular view update");
-            }
-        } else {
-            panic!("Window does not exist");
-        };
+        let mut view = self
+            .ctx
+            .views
+            .remove(&(handle.window_id, handle.view_id))
+            .expect("circular view update");
 
         let mut ctx = ViewContext::new(self, handle.window_id, handle.view_id);
         let result = update(
             view.as_any_mut()
                 .downcast_mut()
-                .expect("Downcast is type safe"),
+                .expect("downcast is type safe"),
             &mut ctx,
         );
         self.ctx
-            .windows
-            .get_mut(&handle.window_id)
-            .unwrap()
             .views
-            .insert(handle.view_id, view);
+            .insert((handle.window_id, handle.view_id), view);
         self.flush_effects();
         result
     }
@@ -1374,6 +1344,7 @@ impl AsRef<AppContext> for MutableAppContext {
 
 pub struct AppContext {
     models: HashMap<usize, Box<dyn AnyModel>>,
+    views: HashMap<(usize, usize), Box<dyn AnyView>>,
     windows: HashMap<usize, Window>,
     values: RwLock<HashMap<(TypeId, usize), Box<dyn Any>>>,
     background: Arc<executor::Background>,
@@ -1386,33 +1357,33 @@ impl AppContext {
     pub fn root_view_id(&self, window_id: usize) -> Option<usize> {
         self.windows
             .get(&window_id)
-            .and_then(|window| window.root_view.as_ref().map(|v| v.id()))
+            .map(|window| window.root_view.id())
     }
 
     pub fn focused_view_id(&self, window_id: usize) -> Option<usize> {
         self.windows
             .get(&window_id)
-            .and_then(|window| window.focused_view)
+            .map(|window| window.focused_view_id)
     }
 
     pub fn render_view(&self, window_id: usize, view_id: usize) -> Result<ElementBox> {
-        self.windows
-            .get(&window_id)
-            .and_then(|w| w.views.get(&view_id))
+        self.views
+            .get(&(window_id, view_id))
             .map(|v| v.render(self))
             .ok_or(anyhow!("view not found"))
     }
 
-    pub fn render_views(&self, window_id: usize) -> Result<HashMap<usize, ElementBox>> {
-        self.windows
-            .get(&window_id)
-            .map(|w| {
-                w.views
-                    .iter()
-                    .map(|(id, view)| (*id, view.render(self)))
-                    .collect::<HashMap<_, ElementBox>>()
+    pub fn render_views(&self, window_id: usize) -> HashMap<usize, ElementBox> {
+        self.views
+            .iter()
+            .filter_map(|((win_id, view_id), view)| {
+                if *win_id == window_id {
+                    Some((*view_id, view.render(self)))
+                } else {
+                    None
+                }
             })
-            .ok_or(anyhow!("window not found"))
+            .collect::<HashMap<_, ElementBox>>()
     }
 
     pub fn background_executor(&self) -> &Arc<executor::Background> {
@@ -1450,25 +1421,20 @@ impl ReadModel for AppContext {
 
 impl ReadView for AppContext {
     fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
-        if let Some(window) = self.windows.get(&handle.window_id) {
-            if let Some(view) = window.views.get(&handle.view_id) {
-                view.as_any()
-                    .downcast_ref()
-                    .expect("downcast should be type safe")
-            } else {
-                panic!("circular view reference");
-            }
+        if let Some(view) = self.views.get(&(handle.window_id, handle.view_id)) {
+            view.as_any()
+                .downcast_ref()
+                .expect("downcast should be type safe")
         } else {
-            panic!("window does not exist");
+            panic!("circular view reference");
         }
     }
 }
 
-#[derive(Default)]
 struct Window {
-    views: HashMap<usize, Box<dyn AnyView>>,
-    root_view: Option<AnyViewHandle>,
-    focused_view: Option<usize>,
+    root_view: AnyViewHandle,
+    focused_view_id: usize,
+    invalidation: Option<WindowInvalidation>,
 }
 
 #[derive(Default, Clone)]
@@ -1665,13 +1631,20 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         U: 'static,
     {
         let handle = self.handle();
-        let task = self.app.spawn::<S, U>(future);
+        let weak_handle = handle.downgrade();
+        let task = self
+            .app
+            .spawn::<S, U>(Spawner::Model(handle.into()), future);
 
         self.app.future_handlers.borrow_mut().insert(
             task.id,
             Box::new(move |output, ctx| {
-                let output = *output.downcast().unwrap();
-                handle.update(ctx, |model, ctx| Box::new(callback(model, output, ctx)))
+                weak_handle.upgrade(ctx.as_ref()).map(|handle| {
+                    let output = *output.downcast().unwrap();
+                    handle.update(ctx, |model, ctx| {
+                        Box::new(callback(model, output, ctx)) as Box<dyn Any>
+                    })
+                })
             }),
         );
 
@@ -1691,23 +1664,31 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         U: 'static + Any,
     {
         let handle = self.handle();
-        let task = self.app.spawn_stream(stream);
+        let weak_handle = handle.downgrade();
+        let task = self.app.spawn_stream(Spawner::Model(handle.into()), stream);
 
         self.app.stream_handlers.borrow_mut().insert(
             task.id,
             StreamHandler {
                 item_callback: {
-                    let handle = handle.clone();
+                    let weak_handle = weak_handle.clone();
                     Box::new(move |output, app| {
-                        let output = *output.downcast().unwrap();
-                        handle.update(app, |model, ctx| {
-                            item_callback(model, output, ctx);
-                            ctx.halt_stream
-                        })
+                        if let Some(handle) = weak_handle.upgrade(app.as_ref()) {
+                            let output = *output.downcast().unwrap();
+                            handle.update(app, |model, ctx| {
+                                item_callback(model, output, ctx);
+                                ctx.halt_stream
+                            })
+                        } else {
+                            true
+                        }
                     })
                 },
                 done_callback: Box::new(move |app| {
-                    handle.update(app, |model, ctx| Box::new(done_callback(model, ctx)))
+                    weak_handle.upgrade(app.as_ref()).map(|handle| {
+                        handle.update(app, |model, ctx| Box::new(done_callback(model, ctx)))
+                            as Box<dyn Any>
+                    })
                 }),
             },
         );
@@ -1974,13 +1955,18 @@ impl<'a, T: View> ViewContext<'a, T> {
         U: 'static,
     {
         let handle = self.handle();
-        let task = self.app.spawn(future);
+        let weak_handle = handle.downgrade();
+        let task = self.app.spawn(Spawner::View(handle.into()), future);
 
         self.app.future_handlers.borrow_mut().insert(
             task.id,
             Box::new(move |output, app| {
-                let output = *output.downcast().unwrap();
-                handle.update(app, |view, ctx| Box::new(callback(view, output, ctx)))
+                weak_handle.upgrade(app.as_ref()).map(|handle| {
+                    let output = *output.downcast().unwrap();
+                    handle.update(app, |view, ctx| {
+                        Box::new(callback(view, output, ctx)) as Box<dyn Any>
+                    })
+                })
             }),
         );
 
@@ -2000,22 +1986,31 @@ impl<'a, T: View> ViewContext<'a, T> {
         U: 'static + Any,
     {
         let handle = self.handle();
-        let task = self.app.spawn_stream(stream);
+        let weak_handle = handle.downgrade();
+        let task = self.app.spawn_stream(Spawner::View(handle.into()), stream);
         self.app.stream_handlers.borrow_mut().insert(
             task.id,
             StreamHandler {
                 item_callback: {
-                    let handle = handle.clone();
-                    Box::new(move |output, app| {
-                        let output = *output.downcast().unwrap();
-                        handle.update(app, |view, ctx| {
-                            item_callback(view, output, ctx);
-                            ctx.halt_stream
-                        })
+                    let weak_handle = weak_handle.clone();
+                    Box::new(move |output, ctx| {
+                        if let Some(handle) = weak_handle.upgrade(ctx.as_ref()) {
+                            let output = *output.downcast().unwrap();
+                            handle.update(ctx, |view, ctx| {
+                                item_callback(view, output, ctx);
+                                ctx.halt_stream
+                            })
+                        } else {
+                            true
+                        }
                     })
                 },
-                done_callback: Box::new(move |app| {
-                    handle.update(app, |view, ctx| Box::new(done_callback(view, ctx)))
+                done_callback: Box::new(move |ctx| {
+                    weak_handle.upgrade(ctx.as_ref()).map(|handle| {
+                        handle.update(ctx, |view, ctx| {
+                            Box::new(done_callback(view, ctx)) as Box<dyn Any>
+                        })
+                    })
                 }),
             },
         );
@@ -2087,7 +2082,7 @@ pub enum EntityLocation {
 pub struct ModelHandle<T> {
     model_id: usize,
     model_type: PhantomData<T>,
-    ref_counts: Weak<Mutex<RefCounts>>,
+    ref_counts: Arc<Mutex<RefCounts>>,
 }
 
 impl<T: Entity> ModelHandle<T> {
@@ -2096,7 +2091,7 @@ impl<T: Entity> ModelHandle<T> {
         Self {
             model_id,
             model_type: PhantomData,
-            ref_counts: Arc::downgrade(ref_counts),
+            ref_counts: ref_counts.clone(),
         }
     }
 
@@ -2185,10 +2180,7 @@ impl<T: Entity> ModelHandle<T> {
 
 impl<T> Clone for ModelHandle<T> {
     fn clone(&self) -> Self {
-        if let Some(ref_counts) = self.ref_counts.upgrade() {
-            ref_counts.lock().inc_entity(self.model_id);
-        }
-
+        self.ref_counts.lock().inc_entity(self.model_id);
         Self {
             model_id: self.model_id,
             model_type: PhantomData,
@@ -2230,9 +2222,7 @@ unsafe impl<T> Sync for ModelHandle<T> {}
 
 impl<T> Drop for ModelHandle<T> {
     fn drop(&mut self) {
-        if let Some(ref_counts) = self.ref_counts.upgrade() {
-            ref_counts.lock().dec_model(self.model_id);
-        }
+        self.ref_counts.lock().dec_model(self.model_id);
     }
 }
 
@@ -2281,7 +2271,7 @@ pub struct ViewHandle<T> {
     window_id: usize,
     view_id: usize,
     view_type: PhantomData<T>,
-    ref_counts: Weak<Mutex<RefCounts>>,
+    ref_counts: Arc<Mutex<RefCounts>>,
 }
 
 impl<T: View> ViewHandle<T> {
@@ -2291,7 +2281,7 @@ impl<T: View> ViewHandle<T> {
             window_id,
             view_id,
             view_type: PhantomData,
-            ref_counts: Arc::downgrade(ref_counts),
+            ref_counts: ref_counts.clone(),
         }
     }
 
@@ -2390,10 +2380,7 @@ impl<T: View> ViewHandle<T> {
 
 impl<T> Clone for ViewHandle<T> {
     fn clone(&self) -> Self {
-        if let Some(ref_counts) = self.ref_counts.upgrade() {
-            ref_counts.lock().inc_entity(self.view_id);
-        }
-
+        self.ref_counts.lock().inc_entity(self.view_id);
         Self {
             window_id: self.window_id,
             view_id: self.view_id,
@@ -2422,9 +2409,9 @@ impl<T> Debug for ViewHandle<T> {
 
 impl<T> Drop for ViewHandle<T> {
     fn drop(&mut self) {
-        if let Some(ref_counts) = self.ref_counts.upgrade() {
-            ref_counts.lock().dec_view(self.window_id, self.view_id);
-        }
+        self.ref_counts
+            .lock()
+            .dec_view(self.window_id, self.view_id);
     }
 }
 
@@ -2438,12 +2425,11 @@ impl<T> Handle<T> for ViewHandle<T> {
     }
 }
 
-#[derive(Clone)]
 pub struct AnyViewHandle {
     window_id: usize,
     view_id: usize,
     view_type: TypeId,
-    ref_counts: Weak<Mutex<RefCounts>>,
+    ref_counts: Arc<Mutex<RefCounts>>,
 }
 
 impl AnyViewHandle {
@@ -2457,19 +2443,38 @@ impl AnyViewHandle {
 
     pub fn downcast<T: View>(self) -> Option<ViewHandle<T>> {
         if self.is::<T>() {
-            if let Some(ref_counts) = self.ref_counts.upgrade() {
-                return Some(ViewHandle::new(self.window_id, self.view_id, &ref_counts));
+            let result = Some(ViewHandle {
+                window_id: self.window_id,
+                view_id: self.view_id,
+                ref_counts: self.ref_counts.clone(),
+                view_type: PhantomData,
+            });
+            unsafe {
+                Arc::decrement_strong_count(&self.ref_counts);
             }
+            std::mem::forget(self);
+            result
+        } else {
+            None
+        }
+    }
+}
+
+impl Clone for AnyViewHandle {
+    fn clone(&self) -> Self {
+        self.ref_counts.lock().inc_entity(self.view_id);
+        Self {
+            window_id: self.window_id,
+            view_id: self.view_id,
+            view_type: self.view_type,
+            ref_counts: self.ref_counts.clone(),
         }
-        None
     }
 }
 
 impl<T: View> From<&ViewHandle<T>> for AnyViewHandle {
     fn from(handle: &ViewHandle<T>) -> Self {
-        if let Some(ref_counts) = handle.ref_counts.upgrade() {
-            ref_counts.lock().inc_entity(handle.view_id);
-        }
+        handle.ref_counts.lock().inc_entity(handle.view_id);
         AnyViewHandle {
             window_id: handle.window_id,
             view_id: handle.view_id,
@@ -2481,10 +2486,48 @@ impl<T: View> From<&ViewHandle<T>> for AnyViewHandle {
 
 impl<T: View> From<ViewHandle<T>> for AnyViewHandle {
     fn from(handle: ViewHandle<T>) -> Self {
-        (&handle).into()
+        let any_handle = AnyViewHandle {
+            window_id: handle.window_id,
+            view_id: handle.view_id,
+            view_type: TypeId::of::<T>(),
+            ref_counts: handle.ref_counts.clone(),
+        };
+        unsafe {
+            Arc::decrement_strong_count(&handle.ref_counts);
+        }
+        std::mem::forget(handle);
+        any_handle
+    }
+}
+
+impl Drop for AnyViewHandle {
+    fn drop(&mut self) {
+        self.ref_counts
+            .lock()
+            .dec_view(self.window_id, self.view_id);
+    }
+}
+
+pub struct AnyModelHandle {
+    model_id: usize,
+    ref_counts: Arc<Mutex<RefCounts>>,
+}
+
+impl<T: Entity> From<ModelHandle<T>> for AnyModelHandle {
+    fn from(handle: ModelHandle<T>) -> Self {
+        handle.ref_counts.lock().inc_entity(handle.model_id);
+        Self {
+            model_id: handle.model_id,
+            ref_counts: handle.ref_counts.clone(),
+        }
     }
 }
 
+impl Drop for AnyModelHandle {
+    fn drop(&mut self) {
+        self.ref_counts.lock().dec_model(self.model_id);
+    }
+}
 pub struct WeakViewHandle<T> {
     window_id: usize,
     view_id: usize,
@@ -2502,12 +2545,7 @@ impl<T: View> WeakViewHandle<T> {
 
     pub fn upgrade(&self, ctx: impl AsRef<AppContext>) -> Option<ViewHandle<T>> {
         let ctx = ctx.as_ref();
-        if ctx
-            .windows
-            .get(&self.window_id)
-            .and_then(|w| w.views.get(&self.view_id))
-            .is_some()
-        {
+        if ctx.ref_counts.lock().is_entity_alive(self.view_id) {
             Some(ViewHandle::new(
                 self.window_id,
                 self.view_id,
@@ -2586,8 +2624,8 @@ struct RefCounts {
 }
 
 impl RefCounts {
-    fn inc_entity(&mut self, model_id: usize) {
-        *self.entity_counts.entry(model_id).or_insert(0) += 1;
+    fn inc_entity(&mut self, entity_id: usize) {
+        *self.entity_counts.entry(entity_id).or_insert(0) += 1;
     }
 
     fn inc_value(&mut self, tag_type_id: TypeId, id: usize) {
@@ -2622,6 +2660,10 @@ impl RefCounts {
         }
     }
 
+    fn is_entity_alive(&self, entity_id: usize) -> bool {
+        self.entity_counts.contains_key(&entity_id)
+    }
+
     fn take_dropped(
         &mut self,
     ) -> (
@@ -2669,20 +2711,26 @@ struct ViewObservation {
     callback: Box<dyn FnMut(&mut dyn Any, usize, usize, &mut MutableAppContext, usize, usize)>,
 }
 
-type FutureHandler = Box<dyn FnOnce(Box<dyn Any>, &mut MutableAppContext) -> Box<dyn Any>>;
+type FutureHandler = Box<dyn FnOnce(Box<dyn Any>, &mut MutableAppContext) -> Option<Box<dyn Any>>>;
 
 struct StreamHandler {
     item_callback: Box<dyn FnMut(Box<dyn Any>, &mut MutableAppContext) -> bool>,
-    done_callback: Box<dyn FnOnce(&mut MutableAppContext) -> Box<dyn Any>>,
+    done_callback: Box<dyn FnOnce(&mut MutableAppContext) -> Option<Box<dyn Any>>>,
 }
 
 #[must_use]
 pub struct EntityTask<T> {
     id: usize,
-    task: Option<executor::Task<T>>,
+    task: Option<executor::Task<Option<T>>>,
+    _spawner: Spawner, // Keeps the spawning entity alive for as long as the task exists
     handler_map: TaskHandlerMap,
 }
 
+pub enum Spawner {
+    Model(AnyModelHandle),
+    View(AnyViewHandle),
+}
+
 enum TaskHandlerMap {
     Detached,
     Future(Rc<RefCell<HashMap<usize, FutureHandler>>>),

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

@@ -347,7 +347,20 @@ impl platform::Platform for MacPlatform {
     }
 
     fn quit(&self) {
+        // Quitting the app causes us to close windows, which invokes `Window::on_close` callbacks
+        // synchronously before this method terminates. If we call `Platform::quit` while holding a
+        // borrow of the app state (which most of the time we will do), we will end up
+        // double-borrowing the app state in the `on_close` callbacks for our open windows. To solve
+        // this, we make quitting the application asynchronous so that we aren't holding borrows to
+        // the app state on the stack when we actually terminate the app.
+
+        use super::dispatcher::{dispatch_async_f, dispatch_get_main_queue};
+
         unsafe {
+            dispatch_async_f(dispatch_get_main_queue(), ptr::null_mut(), Some(quit));
+        }
+
+        unsafe extern "C" fn quit(_: *mut c_void) {
             let app = NSApplication::sharedApplication(nil);
             let _: () = msg_send![app, terminate: nil];
         }

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

@@ -62,6 +62,7 @@ unsafe fn build_classes() {
             sel!(sendEvent:),
             send_event as extern "C" fn(&Object, Sel, id),
         );
+        decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
         decl.register()
     };
 
@@ -126,6 +127,7 @@ struct WindowState {
     native_window: id,
     event_callback: Option<Box<dyn FnMut(Event)>>,
     resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
+    close_callback: Option<Box<dyn FnOnce()>>,
     synthetic_drag_counter: usize,
     executor: Rc<executor::Foreground>,
     scene_to_render: Option<Scene>,
@@ -186,6 +188,7 @@ impl Window {
                 native_window,
                 event_callback: None,
                 resize_callback: None,
+                close_callback: None,
                 synthetic_drag_counter: 0,
                 executor,
                 scene_to_render: Default::default(),
@@ -265,6 +268,10 @@ impl platform::Window for Window {
     fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn platform::WindowContext)>) {
         self.0.as_ref().borrow_mut().resize_callback = Some(callback);
     }
+
+    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
+        self.0.as_ref().borrow_mut().close_callback = Some(callback);
+    }
 }
 
 impl platform::WindowContext for Window {
@@ -313,7 +320,7 @@ unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
 
 unsafe fn drop_window_state(object: &Object) {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    Rc::from_raw(raw as *mut WindowState);
+    Rc::from_raw(raw as *mut RefCell<WindowState>);
 }
 
 extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
@@ -392,6 +399,25 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
     }
 }
 
+extern "C" fn close_window(this: &Object, _: Sel) {
+    unsafe {
+        let close_callback = {
+            let window_state = get_window_state(this);
+            window_state
+                .as_ref()
+                .try_borrow_mut()
+                .ok()
+                .and_then(|mut window_state| window_state.close_callback.take())
+        };
+
+        if let Some(callback) = close_callback {
+            callback();
+        }
+
+        let () = msg_send![super(this, class!(NSWindow)), close];
+    }
+}
+
 extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
     let window_state = unsafe { get_window_state(this) };
     let window_state = window_state.as_ref().borrow();

gpui/src/platform/mod.rs 🔗

@@ -70,6 +70,7 @@ pub trait Dispatcher: Send + Sync {
 pub trait Window: WindowContext {
     fn on_event(&mut self, callback: Box<dyn FnMut(Event)>);
     fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn WindowContext)>);
+    fn on_close(&mut self, callback: Box<dyn FnOnce()>);
 }
 
 pub trait WindowContext {

gpui/src/platform/test.rs 🔗

@@ -23,6 +23,7 @@ pub struct Window {
     current_scene: Option<crate::Scene>,
     event_handlers: Vec<Box<dyn FnMut(super::Event)>>,
     resize_handlers: Vec<Box<dyn FnMut(&mut dyn super::WindowContext)>>,
+    close_handlers: Vec<Box<dyn FnOnce()>>,
 }
 
 impl Platform {
@@ -119,6 +120,7 @@ impl Window {
             size,
             event_handlers: Vec::new(),
             resize_handlers: Vec::new(),
+            close_handlers: Vec::new(),
             scale_factor: 1.0,
             current_scene: None,
         }
@@ -157,6 +159,10 @@ impl super::Window for Window {
     fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn super::WindowContext)>) {
         self.resize_handlers.push(callback);
     }
+
+    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
+        self.close_handlers.push(callback);
+    }
 }
 
 pub(crate) fn platform() -> Platform {

gpui/src/presenter.rs 🔗

@@ -35,7 +35,7 @@ impl Presenter {
     ) -> Self {
         Self {
             window_id,
-            rendered_views: app.render_views(window_id).unwrap(),
+            rendered_views: app.render_views(window_id),
             parents: HashMap::new(),
             font_cache,
             text_layout_cache,
@@ -55,15 +55,16 @@ impl Presenter {
         path
     }
 
-    pub fn invalidate(&mut self, invalidation: WindowInvalidation, app: &AppContext) {
-        for view_id in invalidation.updated {
-            self.rendered_views
-                .insert(view_id, app.render_view(self.window_id, view_id).unwrap());
-        }
+    pub fn invalidate(&mut self, mut invalidation: WindowInvalidation, app: &AppContext) {
         for view_id in invalidation.removed {
+            invalidation.updated.remove(&view_id);
             self.rendered_views.remove(&view_id);
             self.parents.remove(&view_id);
         }
+        for view_id in invalidation.updated {
+            self.rendered_views
+                .insert(view_id, app.render_view(self.window_id, view_id).unwrap());
+        }
     }
 
     pub fn build_scene(

zed/src/editor/buffer_element.rs 🔗

@@ -9,7 +9,7 @@ use gpui::{
     json::{self, ToJson},
     text_layout::{self, TextLayoutCache},
     AfterLayoutContext, AppContext, Border, Element, Event, EventContext, FontCache, LayoutContext,
-    PaintContext, Quad, Scene, SizeConstraint, ViewHandle,
+    PaintContext, Quad, Scene, SizeConstraint, WeakViewHandle,
 };
 use json::json;
 use smallvec::SmallVec;
@@ -20,14 +20,18 @@ use std::{
 };
 
 pub struct BufferElement {
-    view: ViewHandle<BufferView>,
+    view: WeakViewHandle<BufferView>,
 }
 
 impl BufferElement {
-    pub fn new(view: ViewHandle<BufferView>) -> Self {
+    pub fn new(view: WeakViewHandle<BufferView>) -> Self {
         Self { view }
     }
 
+    fn view<'a>(&self, ctx: &'a AppContext) -> &'a BufferView {
+        self.view.upgrade(ctx).unwrap().read(ctx)
+    }
+
     fn mouse_down(
         &self,
         position: Vector2F,
@@ -37,7 +41,7 @@ impl BufferElement {
         ctx: &mut EventContext,
     ) -> bool {
         if paint.text_bounds.contains_point(position) {
-            let view = self.view.read(ctx.app);
+            let view = self.view(ctx.app);
             let position =
                 paint.point_for_position(view, layout, position, ctx.font_cache, ctx.app);
             ctx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd });
@@ -48,7 +52,7 @@ impl BufferElement {
     }
 
     fn mouse_up(&self, _position: Vector2F, ctx: &mut EventContext) -> bool {
-        if self.view.read(ctx.app).is_selecting() {
+        if self.view(ctx.app).is_selecting() {
             ctx.dispatch_action("buffer:select", SelectAction::End);
             true
         } else {
@@ -63,7 +67,7 @@ impl BufferElement {
         paint: &mut PaintState,
         ctx: &mut EventContext,
     ) -> bool {
-        let view = self.view.read(ctx.app);
+        let view = self.view(ctx.app);
 
         if view.is_selecting() {
             let rect = paint.text_bounds;
@@ -116,7 +120,9 @@ impl BufferElement {
     }
 
     fn key_down(&self, chars: &str, ctx: &mut EventContext) -> bool {
-        if self.view.is_focused(ctx.app) {
+        let view = self.view.upgrade(ctx.app).unwrap();
+
+        if view.is_focused(ctx.app) {
             if chars.is_empty() {
                 false
             } else {
@@ -145,7 +151,7 @@ impl BufferElement {
             return false;
         }
 
-        let view = self.view.read(ctx.app);
+        let view = self.view(ctx.app);
         let font_cache = &ctx.font_cache;
         let layout_cache = &ctx.text_layout_cache;
         let max_glyph_width = view.em_width(font_cache);
@@ -167,7 +173,7 @@ impl BufferElement {
     }
 
     fn paint_gutter(&mut self, rect: RectF, layout: &LayoutState, ctx: &mut PaintContext) {
-        let view = self.view.read(ctx.app);
+        let view = self.view(ctx.app);
         let line_height = view.line_height(ctx.font_cache);
         let scroll_top = view.scroll_position().y() * line_height;
 
@@ -197,7 +203,7 @@ impl BufferElement {
     }
 
     fn paint_text(&mut self, bounds: RectF, layout: &LayoutState, ctx: &mut PaintContext) {
-        let view = self.view.read(ctx.app);
+        let view = self.view(ctx.app);
         let line_height = view.line_height(ctx.font_cache);
         let descent = view.font_descent(ctx.font_cache);
         let start_row = view.scroll_position().y() as u32;
@@ -313,14 +319,14 @@ impl Element for BufferElement {
         let app = ctx.app;
         let mut size = constraint.max;
         if size.y().is_infinite() {
-            let view = self.view.read(app);
+            let view = self.view(app);
             size.set_y((view.max_point(app).row() + 1) as f32 * view.line_height(ctx.font_cache));
         }
         if size.x().is_infinite() {
             unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
         }
 
-        let view = self.view.read(app);
+        let view = self.view(app);
         let font_cache = &ctx.font_cache;
         let layout_cache = &ctx.text_layout_cache;
         let line_height = view.line_height(font_cache);
@@ -404,7 +410,7 @@ impl Element for BufferElement {
         if let Some(layout) = layout {
             let app = ctx.app.as_ref();
 
-            let view = self.view.read(app);
+            let view = self.view(app);
             view.clamp_scroll_left(
                 layout
                     .scroll_max(view, ctx.font_cache, ctx.text_layout_cache, app)
@@ -437,7 +443,7 @@ impl Element for BufferElement {
                 layout.text_size,
             );
 
-            if self.view.read(ctx.app).is_gutter_visible() {
+            if self.view(ctx.app).is_gutter_visible() {
                 self.paint_gutter(gutter_bounds, layout, ctx);
             }
             self.paint_text(text_bounds, layout, ctx);

zed/src/editor/buffer_view.rs 🔗

@@ -2,7 +2,7 @@ use super::{
     buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point,
     Selection, SelectionSetId, ToOffset, ToPoint,
 };
-use crate::{settings::Settings, watch, workspace, worktree::FileHandle};
+use crate::{settings::Settings, workspace, worktree::FileHandle};
 use anyhow::Result;
 use futures_core::future::LocalBoxFuture;
 use gpui::{
@@ -11,6 +11,7 @@ use gpui::{
     MutableAppContext, TextLayoutCache, View, ViewContext, WeakViewHandle,
 };
 use parking_lot::Mutex;
+use postage::watch;
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
 use smol::Timer;
@@ -300,15 +301,9 @@ impl BufferView {
         settings: watch::Receiver<Settings>,
         ctx: &mut ViewContext<Self>,
     ) -> Self {
-        settings.notify_view_on_change(ctx);
-
         ctx.observe_model(&buffer, Self::on_buffer_changed);
         ctx.subscribe_to_model(&buffer, Self::on_buffer_event);
-        let display_map = DisplayMap::new(
-            buffer.clone(),
-            smol::block_on(settings.read()).tab_size,
-            ctx.as_ref(),
-        );
+        let display_map = DisplayMap::new(buffer.clone(), settings.borrow().tab_size, ctx.as_ref());
 
         let (selection_set_id, _) = buffer.update(ctx, |buffer, ctx| {
             buffer.add_selection_set(
@@ -1989,31 +1984,31 @@ impl BufferView {
     }
 
     pub fn font_size(&self) -> f32 {
-        smol::block_on(self.settings.read()).buffer_font_size
+        self.settings.borrow().buffer_font_size
     }
 
     pub fn font_ascent(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         let ascent = font_cache.metric(font_id, |m| m.ascent);
         font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
     }
 
     pub fn font_descent(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         let ascent = font_cache.metric(font_id, |m| m.descent);
         font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
     }
 
     pub fn line_height(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         font_cache.line_height(font_id, settings.buffer_font_size)
     }
 
     pub fn em_width(&self, font_cache: &FontCache) -> f32 {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id = font_cache.default_font(settings.buffer_font_family);
         font_cache.em_width(font_id, settings.buffer_font_size)
     }
@@ -2025,7 +2020,7 @@ impl BufferView {
         layout_cache: &TextLayoutCache,
         app: &AppContext,
     ) -> Result<f32> {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_size = settings.buffer_font_size;
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
@@ -2050,7 +2045,7 @@ impl BufferView {
         layout_cache: &TextLayoutCache,
         ctx: &AppContext,
     ) -> Result<Vec<Arc<text_layout::Line>>> {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_size = settings.buffer_font_size;
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
@@ -2094,7 +2089,7 @@ impl BufferView {
             return Ok(Vec::new());
         }
 
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
         let font_size = settings.buffer_font_size;
@@ -2132,7 +2127,7 @@ impl BufferView {
         layout_cache: &TextLayoutCache,
         app: &AppContext,
     ) -> Result<Arc<text_layout::Line>> {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let font_id =
             font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
 
@@ -2226,8 +2221,8 @@ impl Entity for BufferView {
 }
 
 impl View for BufferView {
-    fn render<'a>(&self, app: &AppContext) -> ElementBox {
-        BufferElement::new(self.handle.upgrade(app).unwrap()).boxed()
+    fn render<'a>(&self, _: &AppContext) -> ElementBox {
+        BufferElement::new(self.handle.clone()).boxed()
     }
 
     fn ui_name() -> &'static str {

zed/src/file_finder.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     editor::{buffer_view, BufferView},
     settings::Settings,
-    util, watch,
+    util,
     workspace::Workspace,
     worktree::{match_paths, PathMatch, Worktree},
 };
@@ -14,6 +14,7 @@ use gpui::{
     AppContext, Axis, Border, Entity, MutableAppContext, View, ViewContext, ViewHandle,
     WeakViewHandle,
 };
+use postage::watch;
 use std::{
     cmp,
     path::Path,
@@ -105,7 +106,7 @@ impl View for FileFinder {
 impl FileFinder {
     fn render_matches(&self) -> ElementBox {
         if self.matches.is_empty() {
-            let settings = smol::block_on(self.settings.read());
+            let settings = self.settings.borrow();
             return Container::new(
                 Label::new(
                     "No matches".into(),
@@ -148,7 +149,7 @@ impl FileFinder {
     ) -> Option<ElementBox> {
         self.labels_for_match(path_match, app).map(
             |(file_name, file_name_positions, full_path, full_path_positions)| {
-                let settings = smol::block_on(self.settings.read());
+                let settings = self.settings.borrow();
                 let highlight_color = ColorU::from_u32(0x304ee2ff);
                 let bold = *Properties::new().weight(Weight::BOLD);
                 let mut container = Container::new(
@@ -292,8 +293,6 @@ impl FileFinder {
         let query_buffer = ctx.add_view(|ctx| BufferView::single_line(settings.clone(), ctx));
         ctx.subscribe_to_view(&query_buffer, Self::on_query_buffer_event);
 
-        settings.notify_view_on_change(ctx);
-
         Self {
             handle: ctx.handle().downgrade(),
             settings,

zed/src/lib.rs 🔗

@@ -9,6 +9,5 @@ mod sum_tree;
 mod test;
 mod time;
 mod util;
-pub mod watch;
 pub mod workspace;
 mod worktree;

zed/src/menus.rs 🔗

@@ -1,8 +1,9 @@
-use crate::{settings::Settings, watch::Receiver};
+use crate::settings::Settings;
 use gpui::{Menu, MenuItem};
+use postage::watch;
 
 #[cfg(target_os = "macos")]
-pub fn menus(settings: Receiver<Settings>) -> Vec<Menu<'static>> {
+pub fn menus(settings: watch::Receiver<Settings>) -> Vec<Menu<'static>> {
     vec![
         Menu {
             name: "Zed",

zed/src/settings.rs 🔗

@@ -1,6 +1,6 @@
-use crate::watch;
 use anyhow::Result;
 use gpui::font_cache::{FamilyId, FontCache};
+use postage::watch;
 
 #[derive(Clone)]
 pub struct Settings {
@@ -26,5 +26,5 @@ impl Settings {
 pub fn channel(
     font_cache: &FontCache,
 ) -> Result<(watch::Sender<Settings>, watch::Receiver<Settings>)> {
-    Ok(watch::channel(Settings::new(font_cache)?))
+    Ok(watch::channel_with(Settings::new(font_cache)?))
 }

zed/src/watch.rs 🔗

@@ -1,65 +0,0 @@
-// TODO: This implementation is actually broken in that it will only
-
-use gpui::{Entity, ModelContext, View, ViewContext};
-use smol::{channel, lock::RwLock};
-use std::ops::Deref;
-use std::sync::Arc;
-
-pub struct Sender<T> {
-    value: Arc<RwLock<T>>,
-    updated: channel::Sender<()>,
-}
-
-#[derive(Clone)]
-pub struct Receiver<T> {
-    value: Arc<RwLock<T>>,
-    updated: channel::Receiver<()>,
-}
-
-impl<T> Sender<T> {
-    pub async fn update<R>(&mut self, f: impl FnOnce(&mut T) -> R) -> R {
-        let result = f(&mut *self.value.write().await);
-        self.updated.send(()).await.unwrap();
-        result
-    }
-}
-
-impl<T> Receiver<T> {
-    pub async fn updated(&self) {
-        let _ = self.updated.recv().await;
-    }
-
-    pub async fn read<'a>(&'a self) -> impl 'a + Deref<Target = T> {
-        self.value.read().await
-    }
-}
-
-// TODO: These implementations are broken because they only handle a single update.
-impl<T: 'static + Clone> Receiver<T> {
-    pub fn notify_model_on_change<M: 'static + Entity>(&self, ctx: &mut ModelContext<M>) {
-        let watch = self.clone();
-        ctx.spawn(async move { watch.updated().await }, |_, _, ctx| {
-            ctx.notify()
-        })
-        .detach();
-    }
-
-    pub fn notify_view_on_change<V: 'static + View>(&self, ctx: &mut ViewContext<V>) {
-        let watch = self.clone();
-        ctx.spawn(async move { watch.updated().await }, |_, _, ctx| {
-            ctx.notify()
-        })
-        .detach();
-    }
-}
-
-pub fn channel<T>(value: T) -> (Sender<T>, Receiver<T>) {
-    let value = Arc::new(RwLock::new(value));
-    let (s, r) = channel::unbounded();
-    let sender = Sender {
-        value: value.clone(),
-        updated: s,
-    };
-    let receiver = Receiver { value, updated: r };
-    (sender, receiver)
-}

zed/src/workspace.rs 🔗

@@ -4,7 +4,6 @@ use crate::{
     editor::{Buffer, BufferView},
     settings::Settings,
     time::ReplicaId,
-    watch::{self, Receiver},
     worktree::{FileHandle, Worktree, WorktreeHandle},
 };
 use futures_core::{future::LocalBoxFuture, Future};
@@ -16,6 +15,7 @@ use gpui::{
 use log::error;
 pub use pane::*;
 pub use pane_group::*;
+use postage::watch;
 use smol::prelude::*;
 use std::{collections::HashMap, path::PathBuf};
 use std::{
@@ -43,7 +43,7 @@ pub struct OpenParams {
     pub settings: watch::Receiver<Settings>,
 }
 
-fn open(settings: &Receiver<Settings>, ctx: &mut MutableAppContext) {
+fn open(settings: &watch::Receiver<Settings>, ctx: &mut MutableAppContext) {
     let settings = settings.clone();
     ctx.prompt_for_paths(
         PathPromptOptions {
@@ -182,7 +182,7 @@ impl<T: Item> WeakItemHandle for WeakModelHandle<T> {
     fn add_view(
         &self,
         window_id: usize,
-        settings: Receiver<Settings>,
+        settings: watch::Receiver<Settings>,
         ctx: &mut MutableAppContext,
     ) -> Option<Box<dyn ItemViewHandle>> {
         if let Some(handle) = self.upgrade(ctx.as_ref()) {

zed/src/workspace/pane.rs 🔗

@@ -1,5 +1,5 @@
 use super::{ItemViewHandle, SplitDirection};
-use crate::{settings::Settings, watch};
+use crate::settings::Settings;
 use gpui::{
     color::ColorU,
     elements::*,
@@ -7,6 +7,7 @@ use gpui::{
     keymap::Binding,
     AppContext, Border, Entity, MutableAppContext, Quad, View, ViewContext,
 };
+use postage::watch;
 use std::{cmp, path::Path, sync::Arc};
 
 pub fn init(app: &mut MutableAppContext) {
@@ -185,7 +186,7 @@ impl Pane {
     }
 
     fn render_tabs(&self, ctx: &AppContext) -> ElementBox {
-        let settings = smol::block_on(self.settings.read());
+        let settings = self.settings.borrow();
         let border_color = ColorU::from_u32(0xdbdbdcff);
         let line_height = ctx.font_cache().line_height(
             ctx.font_cache().default_font(settings.ui_font_family),