Pass a handle to the current view model when spawning

Nathan Sobo created

Most of the time, we'll want a way to get a reference back to the current view or model, so this facilitates that common case.

Change summary

gpui/src/app.rs               | 10 +++++---
zed/src/editor/buffer/mod.rs  |  3 -
zed/src/editor/buffer_view.rs | 10 +++-----
zed/src/file_finder.rs        |  5 +--
zed/src/workspace.rs          | 18 +++++++---------
zed/src/worktree.rs           | 39 ++++++++++++++++++------------------
6 files changed, 41 insertions(+), 44 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -1654,11 +1654,12 @@ impl<'a, T: Entity> ModelContext<'a, T> {
 
     pub fn spawn<F, Fut, S>(&self, f: F) -> Task<S>
     where
-        F: FnOnce(AsyncAppContext) -> Fut,
+        F: FnOnce(ModelHandle<T>, AsyncAppContext) -> Fut,
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
-        self.app.spawn(f)
+        let handle = self.handle();
+        self.app.spawn(|ctx| f(handle, ctx))
     }
 }
 
@@ -1909,11 +1910,12 @@ impl<'a, T: View> ViewContext<'a, T> {
 
     pub fn spawn<F, Fut, S>(&self, f: F) -> Task<S>
     where
-        F: FnOnce(AsyncAppContext) -> Fut,
+        F: FnOnce(ViewHandle<T>, AsyncAppContext) -> Fut,
         Fut: 'static + Future<Output = S>,
         S: 'static,
     {
-        self.app.spawn(f)
+        let handle = self.handle();
+        self.app.spawn(|ctx| f(handle, ctx))
     }
 }
 

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

@@ -477,9 +477,8 @@ impl Buffer {
         let snapshot = self.snapshot();
         let version = self.version.clone();
         let file = self.file.clone();
-        let handle = ctx.handle();
 
-        ctx.spawn(|mut ctx| async move {
+        ctx.spawn(|handle, mut ctx| async move {
             if let Some(file) = new_file.as_ref().or(file.as_ref()) {
                 let result = ctx.read(|ctx| file.save(snapshot, ctx.as_ref())).await;
                 if result.is_ok() {

zed/src/editor/buffer_view.rs 🔗

@@ -2347,10 +2347,9 @@ impl BufferView {
         ctx.notify();
 
         let epoch = self.next_blink_epoch();
-        let handle = ctx.handle();
-        ctx.spawn(|mut ctx| async move {
+        ctx.spawn(|this, mut ctx| async move {
             Timer::after(CURSOR_BLINK_INTERVAL).await;
-            handle.update(&mut ctx, |this, ctx| {
+            this.update(&mut ctx, |this, ctx| {
                 this.resume_cursor_blinking(epoch, ctx);
             })
         })
@@ -2370,10 +2369,9 @@ impl BufferView {
             ctx.notify();
 
             let epoch = self.next_blink_epoch();
-            let handle = ctx.handle();
-            ctx.spawn(|mut ctx| async move {
+            ctx.spawn(|this, mut ctx| async move {
                 Timer::after(CURSOR_BLINK_INTERVAL).await;
-                handle.update(&mut ctx, |this, ctx| this.blink_cursors(epoch, ctx));
+                this.update(&mut ctx, |this, ctx| this.blink_cursors(epoch, ctx));
             })
             .detach();
         }

zed/src/file_finder.rs 🔗

@@ -415,10 +415,9 @@ impl FileFinder {
             (search_id, did_cancel, query, matches)
         });
 
-        let handle = ctx.handle();
-        ctx.spawn(|mut ctx| async move {
+        ctx.spawn(|this, mut ctx| async move {
             let matches = background_task.await;
-            handle.update(&mut ctx, |this, ctx| this.update_matches(matches, ctx));
+            this.update(&mut ctx, |this, ctx| this.update_matches(matches, ctx));
         })
         .detach();
 

zed/src/workspace.rs 🔗

@@ -359,13 +359,12 @@ impl Workspace {
             .cloned()
             .zip(entries.into_iter())
             .map(|(abs_path, file)| {
-                let handle = ctx.handle();
                 let is_file = bg.spawn(async move { abs_path.is_file() });
-                ctx.spawn(|mut ctx| async move {
+                ctx.spawn(|this, mut ctx| async move {
                     let is_file = is_file.await;
-                    handle.update(&mut ctx, |me, ctx| {
+                    this.update(&mut ctx, |this, ctx| {
                         if is_file {
-                            me.open_entry(file.entry_id(), ctx)
+                            this.open_entry(file.entry_id(), ctx)
                         } else {
                             None
                         }
@@ -513,8 +512,7 @@ impl Workspace {
 
         let mut watch = self.loading_items.get(&entry).unwrap().clone();
 
-        let handle = ctx.handle();
-        Some(ctx.spawn(|mut ctx| async move {
+        Some(ctx.spawn(|this, mut ctx| async move {
             let load_result = loop {
                 if let Some(load_result) = watch.borrow().as_ref() {
                     break load_result.clone();
@@ -522,16 +520,16 @@ impl Workspace {
                 watch.next().await;
             };
 
-            handle.update(&mut ctx, |me, ctx| {
-                me.loading_items.remove(&entry);
+            this.update(&mut ctx, |this, ctx| {
+                this.loading_items.remove(&entry);
                 match load_result {
                     Ok(item) => {
                         let weak_item = item.downgrade();
                         let view = weak_item
                             .add_view(ctx.window_id(), settings, ctx.as_mut())
                             .unwrap();
-                        me.items.push(weak_item);
-                        me.add_item_view(view, ctx);
+                        this.items.push(weak_item);
+                        this.add_item_view(view, ctx);
                     }
                     Err(error) => {
                         log::error!("error opening item: {}", error);

zed/src/worktree.rs 🔗

@@ -98,20 +98,23 @@ impl Worktree {
             scanner.run(event_stream)
         });
 
-        let handle = ctx.handle().downgrade();
-        ctx.spawn(|mut ctx| async move {
-            while let Ok(scan_state) = scan_state_rx.recv().await {
-                let alive = ctx.update(|ctx| {
-                    if let Some(handle) = handle.upgrade(&ctx) {
-                        handle.update(ctx, |this, ctx| this.observe_scan_state(scan_state, ctx));
-                        true
-                    } else {
-                        false
-                    }
-                });
+        ctx.spawn(|this, mut ctx| {
+            let this = this.downgrade();
+            async move {
+                while let Ok(scan_state) = scan_state_rx.recv().await {
+                    let alive = ctx.update(|ctx| {
+                        if let Some(handle) = this.upgrade(&ctx) {
+                            handle
+                                .update(ctx, |this, ctx| this.observe_scan_state(scan_state, ctx));
+                            true
+                        } else {
+                            false
+                        }
+                    });
 
-                if !alive {
-                    break;
+                    if !alive {
+                        break;
+                    }
                 }
             }
         })
@@ -133,10 +136,9 @@ impl Worktree {
     pub fn next_scan_complete(&self, ctx: &mut ModelContext<Self>) -> impl Future<Output = ()> {
         let scan_id = self.snapshot.scan_id;
         let mut scan_state = self.scan_state.1.clone();
-        let handle = ctx.handle();
-        ctx.spawn(|ctx| async move {
+        ctx.spawn(|this, ctx| async move {
             while let Some(scan_state) = scan_state.recv().await {
-                if handle.read_with(&ctx, |this, _| {
+                if this.read_with(&ctx, |this, _| {
                     matches!(scan_state, ScanState::Idle) && this.snapshot.scan_id > scan_id
                 }) {
                     break;
@@ -155,9 +157,8 @@ impl Worktree {
         ctx.notify();
 
         if self.is_scanning() && !self.poll_scheduled {
-            let handle = ctx.handle();
-            ctx.spawn(|mut ctx| async move {
-                handle.update(&mut ctx, |this, ctx| {
+            ctx.spawn(|this, mut ctx| async move {
+                this.update(&mut ctx, |this, ctx| {
                     this.poll_scheduled = false;
                     this.poll_entries(ctx);
                 })