From fa6bd1f9263a9d1b5418b43675f7d453d398d779 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 12 May 2021 15:16:49 -0600 Subject: [PATCH] Introduce AsyncAppContext and simplify spawning Now when you call spawn in various context, you pass an FnOnce that is called with an AsyncAppContext and returns a static future. This allows you to write async code similar to how our tests work, borrowing the guts of the AsyncAppContext when needed to interact, but using normal async await semantics instead of callbacks. Co-Authored-By: Max Brunsfeld --- gpui/src/app.rs | 702 +++++++++++++--------------------- zed/src/editor/buffer/mod.rs | 30 +- zed/src/editor/buffer_view.rs | 31 +- zed/src/file_finder.rs | 9 +- zed/src/workspace.rs | 96 ++--- zed/src/worktree.rs | 49 ++- 6 files changed, 390 insertions(+), 527 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index 446220773a1a6445b4a245ae447fdbd963c3a0fc..3bcccef47c2bfcfeaa849dd7fa02feb6e70cc328 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -8,6 +8,7 @@ use crate::{ AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache, }; use anyhow::{anyhow, Result}; +use async_task::Task; use keymap::MatchResult; use parking_lot::{Mutex, RwLock}; use pathfinder_geometry::{rect::RectF, vector::vec2f}; @@ -50,6 +51,14 @@ pub trait ReadModel { fn read_model(&self, handle: &ModelHandle) -> &T; } +pub trait ReadModelWith { + fn read_model_with T, T>( + &self, + handle: &ModelHandle, + read: F, + ) -> T; +} + pub trait UpdateModel { fn update_model(&mut self, handle: &ModelHandle, update: F) -> S where @@ -61,6 +70,13 @@ pub trait ReadView { fn read_view(&self, handle: &ViewHandle) -> &T; } +pub trait ReadViewWith { + fn read_view_with(&self, handle: &ViewHandle, read: F) -> T + where + V: View, + F: FnOnce(&V, &AppContext) -> T; +} + pub trait UpdateView { fn update_view(&mut self, handle: &ViewHandle, update: F) -> S where @@ -86,6 +102,8 @@ pub enum MenuItem<'a> { #[derive(Clone)] pub struct App(Rc>); +pub struct AsyncAppContext(Rc>); + #[derive(Clone)] pub struct TestAppContext(Rc>, Rc); @@ -345,6 +363,80 @@ impl TestAppContext { } } +impl AsyncAppContext { + pub fn read T>(&mut self, callback: F) -> T { + callback(self.0.borrow().as_ref()) + } + + pub fn update T>(&mut self, callback: F) -> T { + let mut state = self.0.borrow_mut(); + state.pending_flushes += 1; + let result = callback(&mut *state); + state.flush_effects(); + result + } + + pub fn add_model(&mut self, build_model: F) -> ModelHandle + where + T: Entity, + F: FnOnce(&mut ModelContext) -> T, + { + self.update(|ctx| ctx.add_model(build_model)) + } +} + +impl UpdateModel for AsyncAppContext { + fn update_model(&mut self, handle: &ModelHandle, update: F) -> S + where + T: Entity, + F: FnOnce(&mut T, &mut ModelContext) -> S, + { + let mut state = self.0.borrow_mut(); + state.pending_flushes += 1; + let result = state.update_model(handle, update); + state.flush_effects(); + result + } +} + +impl ReadModelWith for AsyncAppContext { + fn read_model_with T, T>( + &self, + handle: &ModelHandle, + read: F, + ) -> T { + let ctx = self.0.borrow(); + let ctx = ctx.as_ref(); + read(handle.read(ctx), ctx) + } +} + +impl UpdateView for AsyncAppContext { + fn update_view(&mut self, handle: &ViewHandle, update: F) -> S + where + T: View, + F: FnOnce(&mut T, &mut ViewContext) -> S, + { + let mut state = self.0.borrow_mut(); + state.pending_flushes += 1; + let result = state.update_view(handle, update); + state.flush_effects(); + result + } +} + +impl ReadViewWith for AsyncAppContext { + fn read_view_with(&self, handle: &ViewHandle, read: F) -> T + where + V: View, + F: FnOnce(&V, &AppContext) -> T, + { + let ctx = self.0.borrow(); + let ctx = ctx.as_ref(); + read(handle.read(ctx), ctx) + } +} + impl UpdateModel for TestAppContext { fn update_model(&mut self, handle: &ModelHandle, update: F) -> S where @@ -359,6 +451,18 @@ impl UpdateModel for TestAppContext { } } +impl ReadModelWith for TestAppContext { + fn read_model_with T, T>( + &self, + handle: &ModelHandle, + read: F, + ) -> T { + let ctx = self.0.borrow(); + let ctx = ctx.as_ref(); + read(handle.read(ctx), ctx) + } +} + impl UpdateView for TestAppContext { fn update_view(&mut self, handle: &ViewHandle, update: F) -> S where @@ -373,6 +477,18 @@ impl UpdateView for TestAppContext { } } +impl ReadViewWith for TestAppContext { + fn read_view_with(&self, handle: &ViewHandle, read: F) -> T + where + V: View, + F: FnOnce(&V, &AppContext) -> T, + { + let ctx = self.0.borrow(); + let ctx = ctx.as_ref(); + read(handle.read(ctx), ctx) + } +} + type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Any, &mut MutableAppContext, usize, usize) -> bool; @@ -388,7 +504,6 @@ pub struct MutableAppContext { keystroke_matcher: keymap::Matcher, next_entity_id: usize, next_window_id: usize, - next_task_id: usize, subscriptions: HashMap>, model_observations: HashMap>, view_observations: HashMap>, @@ -396,8 +511,6 @@ pub struct MutableAppContext { HashMap>, Box)>, debug_elements_callbacks: HashMap crate::json::Value>>, foreground: Rc, - future_handlers: Rc>>, - stream_handlers: Rc>>, pending_effects: VecDeque, pending_flushes: usize, flushing_effects: bool, @@ -429,15 +542,12 @@ impl MutableAppContext { keystroke_matcher: keymap::Matcher::default(), next_entity_id: 0, next_window_id: 0, - next_task_id: 0, subscriptions: HashMap::new(), model_observations: HashMap::new(), view_observations: HashMap::new(), presenters_and_platform_windows: HashMap::new(), debug_elements_callbacks: HashMap::new(), foreground, - future_handlers: Default::default(), - stream_handlers: Default::default(), pending_effects: VecDeque::new(), pending_flushes: 0, flushing_effects: false, @@ -1159,96 +1269,14 @@ impl MutableAppContext { self.flush_effects(); } - fn spawn(&mut self, spawner: Spawner, future: F) -> EntityTask + pub fn spawn(&self, f: F) -> Task where - F: 'static + Future, + F: FnOnce(AsyncAppContext) -> Fut, + Fut: 'static + Future, T: 'static, { - let task_id = post_inc(&mut self.next_task_id); - let app = self.weak_self.as_ref().unwrap().upgrade().unwrap(); - let task = { - let app = app.clone(); - self.foreground.spawn(async move { - let output = future.await; - app.borrow_mut() - .handle_future_output(task_id, Box::new(output)) - .map(|output| *output.downcast::().unwrap()) - }) - }; - EntityTask::new( - task_id, - task, - spawner, - TaskHandlerMap::Future(self.future_handlers.clone()), - ) - } - - fn spawn_stream(&mut self, spawner: Spawner, mut stream: F) -> EntityTask - where - F: 'static + Stream + Unpin, - T: 'static, - { - let task_id = post_inc(&mut self.next_task_id); - let app = self.weak_self.as_ref().unwrap().upgrade().unwrap(); - let task = self.foreground.spawn(async move { - loop { - match stream.next().await { - Some(item) => { - let mut app = app.borrow_mut(); - if app.handle_stream_item(task_id, Box::new(item)) { - break; - } - } - None => { - break; - } - } - } - - app.borrow_mut() - .stream_completed(task_id) - .map(|output| *output.downcast::().unwrap()) - }); - - EntityTask::new( - task_id, - task, - spawner, - TaskHandlerMap::Stream(self.stream_handlers.clone()), - ) - } - - fn handle_future_output( - &mut self, - task_id: usize, - output: Box, - ) -> Option> { - self.pending_flushes += 1; - let future_callback = self.future_handlers.borrow_mut().remove(&task_id).unwrap(); - let result = future_callback(output, self); - self.flush_effects(); - result - } - - fn handle_stream_item(&mut self, task_id: usize, output: Box) -> bool { - self.pending_flushes += 1; - - let mut handler = self.stream_handlers.borrow_mut().remove(&task_id).unwrap(); - let halt = (handler.item_callback)(output, self); - self.stream_handlers.borrow_mut().insert(task_id, handler); - - self.flush_effects(); - halt - } - - fn stream_completed(&mut self, task_id: usize) -> Option> { - self.pending_flushes += 1; - - let handler = self.stream_handlers.borrow_mut().remove(&task_id).unwrap(); - let result = (handler.done_callback)(self); - - self.flush_effects(); - result + let ctx = AsyncAppContext(self.weak_self.as_ref().unwrap().upgrade().unwrap()); + self.foreground.spawn(f(ctx)) } pub fn write_to_clipboard(&self, item: ClipboardItem) { @@ -1624,76 +1652,13 @@ impl<'a, T: Entity> ModelContext<'a, T> { ModelHandle::new(self.model_id, &self.app.ctx.ref_counts) } - pub fn spawn(&mut self, future: S, callback: F) -> EntityTask - where - S: 'static + Future, - F: 'static + FnOnce(&mut T, S::Output, &mut ModelContext) -> U, - U: 'static, - { - let handle = self.handle(); - let weak_handle = handle.downgrade(); - let task = self - .app - .spawn::(Spawner::Model(handle.into()), future); - - self.app.future_handlers.borrow_mut().insert( - task.id, - Box::new(move |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 - }) - }) - }), - ); - - task - } - - pub fn spawn_stream( - &mut self, - stream: S, - mut item_callback: F, - done_callback: G, - ) -> EntityTask + pub fn spawn(&self, f: F) -> Task where - S: 'static + Stream + Unpin, - F: 'static + FnMut(&mut T, S::Item, &mut ModelContext), - G: 'static + FnOnce(&mut T, &mut ModelContext) -> U, - U: 'static + Any, + F: FnOnce(AsyncAppContext) -> Fut, + Fut: 'static + Future, + S: 'static, { - let handle = self.handle(); - 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 weak_handle = weak_handle.clone(); - Box::new(move |output, app| { - 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| { - weak_handle.upgrade(app.as_ref()).map(|handle| { - handle.update(app, |model, ctx| Box::new(done_callback(model, ctx))) - as Box - }) - }), - }, - ); - - task + self.app.spawn(f) } } @@ -1731,7 +1696,6 @@ pub struct ViewContext<'a, T: ?Sized> { view_id: usize, view_type: PhantomData, halt_action_dispatch: bool, - halt_stream: bool, } impl<'a, T: View> ViewContext<'a, T> { @@ -1742,7 +1706,6 @@ impl<'a, T: View> ViewContext<'a, T> { view_id, view_type: PhantomData, halt_action_dispatch: true, - halt_stream: false, } } @@ -1944,77 +1907,13 @@ impl<'a, T: View> ViewContext<'a, T> { self.halt_action_dispatch = false; } - pub fn halt_stream(&mut self) { - self.halt_stream = true; - } - - pub fn spawn(&mut self, future: S, callback: F) -> EntityTask + pub fn spawn(&self, f: F) -> Task where - S: 'static + Future, - F: 'static + FnOnce(&mut T, S::Output, &mut ViewContext) -> U, - U: 'static, + F: FnOnce(AsyncAppContext) -> Fut, + Fut: 'static + Future, + S: 'static, { - let handle = self.handle(); - 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| { - 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 - }) - }) - }), - ); - - task - } - - pub fn spawn_stream( - &mut self, - stream: S, - mut item_callback: F, - done_callback: G, - ) -> EntityTask - where - S: 'static + Stream + Unpin, - F: 'static + FnMut(&mut T, S::Item, &mut ViewContext), - G: 'static + FnOnce(&mut T, &mut ViewContext) -> U, - U: 'static + Any, - { - let handle = self.handle(); - 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 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 |ctx| { - weak_handle.upgrade(ctx.as_ref()).map(|handle| { - handle.update(ctx, |view, ctx| { - Box::new(done_callback(view, ctx)) as Box - }) - }) - }), - }, - ); - task + self.app.spawn(f) } } @@ -2107,6 +2006,14 @@ impl ModelHandle { app.read_model(self) } + pub fn read_with<'a, A, F, S>(&self, ctx: &A, read: F) -> S + where + A: ReadModelWith, + F: FnOnce(&T, &AppContext) -> S, + { + ctx.read_model_with(self, read) + } + pub fn update(&self, app: &mut A, update: F) -> S where A: UpdateModel, @@ -2249,9 +2156,10 @@ impl WeakModelHandle { } } - pub fn upgrade(&self, app: &AppContext) -> Option> { - if app.models.contains_key(&self.model_id) { - Some(ModelHandle::new(self.model_id, &app.ref_counts)) + pub fn upgrade(&self, ctx: impl AsRef) -> Option> { + let ctx = ctx.as_ref(); + if ctx.models.contains_key(&self.model_id) { + Some(ModelHandle::new(self.model_id, &ctx.ref_counts)) } else { None } @@ -2301,6 +2209,14 @@ impl ViewHandle { app.read_view(self) } + pub fn read_with(&self, ctx: &A, read: F) -> S + where + A: ReadViewWith, + F: FnOnce(&T, &AppContext) -> S, + { + ctx.read_view_with(self, read) + } + pub fn update(&self, app: &mut A, update: F) -> S where A: UpdateView, @@ -2711,86 +2627,6 @@ struct ViewObservation { callback: Box, } -type FutureHandler = Box, &mut MutableAppContext) -> Option>>; - -struct StreamHandler { - item_callback: Box, &mut MutableAppContext) -> bool>, - done_callback: Box Option>>, -} - -#[must_use] -pub struct EntityTask { - id: usize, - task: Option>>, - _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>>), - Stream(Rc>>), -} - -impl EntityTask { - fn new( - id: usize, - task: executor::Task>, - spawner: Spawner, - handler_map: TaskHandlerMap, - ) -> Self { - Self { - id, - task: Some(task), - _spawner: spawner, - handler_map, - } - } - - pub fn detach(mut self) { - self.handler_map = TaskHandlerMap::Detached; - self.task.take().unwrap().detach(); - } - - pub async fn cancel(mut self) -> Option { - let task = self.task.take().unwrap(); - task.cancel().await.unwrap() - } -} - -impl Future for EntityTask { - type Output = T; - - fn poll( - self: std::pin::Pin<&mut Self>, - ctx: &mut std::task::Context<'_>, - ) -> std::task::Poll { - let task = unsafe { self.map_unchecked_mut(|task| task.task.as_mut().unwrap()) }; - task.poll(ctx).map(|output| output.unwrap()) - } -} - -impl Drop for EntityTask { - fn drop(self: &mut Self) { - match &self.handler_map { - TaskHandlerMap::Detached => { - return; - } - TaskHandlerMap::Future(map) => { - map.borrow_mut().remove(&self.id); - } - TaskHandlerMap::Stream(map) => { - map.borrow_mut().remove(&self.id); - } - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -2926,60 +2762,60 @@ mod tests { assert_eq!(handle_1.read(app).events, vec![7, 10, 5]) } - #[crate::test(self)] - async fn test_spawn_from_model(mut app: TestAppContext) { - #[derive(Default)] - struct Model { - count: usize, - } - - impl Entity for Model { - type Event = (); - } + // #[crate::test(self)] + // async fn test_spawn_from_model(mut app: TestAppContext) { + // #[derive(Default)] + // struct Model { + // count: usize, + // } - let handle = app.add_model(|_| Model::default()); - handle - .update(&mut app, |_, c| { - c.spawn(async { 7 }, |model, output, _| { - model.count = output; - }) - }) - .await; - app.read(|ctx| assert_eq!(handle.read(ctx).count, 7)); + // impl Entity for Model { + // type Event = (); + // } - handle - .update(&mut app, |_, c| { - c.spawn(async { 14 }, |model, output, _| { - model.count = output; - }) - }) - .await; - app.read(|ctx| assert_eq!(handle.read(ctx).count, 14)); - } + // let handle = app.add_model(|_| Model::default()); + // handle + // .update(&mut app, |_, c| { + // c.spawn(async { 7 }, |model, output, _| { + // model.count = output; + // }) + // }) + // .await; + // app.read(|ctx| assert_eq!(handle.read(ctx).count, 7)); + + // handle + // .update(&mut app, |_, c| { + // c.spawn(async { 14 }, |model, output, _| { + // model.count = output; + // }) + // }) + // .await; + // app.read(|ctx| assert_eq!(handle.read(ctx).count, 14)); + // } - #[crate::test(self)] - async fn test_spawn_stream_local_from_model(mut app: TestAppContext) { - #[derive(Default)] - struct Model { - events: Vec>, - } + // #[crate::test(self)] + // async fn test_spawn_stream_local_from_model(mut app: TestAppContext) { + // #[derive(Default)] + // struct Model { + // events: Vec>, + // } - impl Entity for Model { - type Event = (); - } + // impl Entity for Model { + // type Event = (); + // } - let handle = app.add_model(|_| Model::default()); - handle - .update(&mut app, |_, c| { - c.spawn_stream( - smol::stream::iter(vec![1, 2, 3]), - |model, output, _| model.events.push(Some(output)), - |model, _| model.events.push(None), - ) - }) - .await; - app.read(|ctx| assert_eq!(handle.read(ctx).events, [Some(1), Some(2), Some(3), None])); - } + // let handle = app.add_model(|_| Model::default()); + // handle + // .update(&mut app, |_, c| { + // c.spawn_stream( + // smol::stream::iter(vec![1, 2, 3]), + // |model, output, _| model.events.push(Some(output)), + // |model, _| model.events.push(None), + // ) + // }) + // .await; + // app.read(|ctx| assert_eq!(handle.read(ctx).events, [Some(1), Some(2), Some(3), None])); + // } #[crate::test(self)] fn test_view_handles(app: &mut MutableAppContext) { @@ -3297,84 +3133,84 @@ mod tests { ); } - #[crate::test(self)] - async fn test_spawn_from_view(mut app: TestAppContext) { - #[derive(Default)] - struct View { - count: usize, - } - - impl Entity for View { - type Event = (); - } + // #[crate::test(self)] + // async fn test_spawn_from_view(mut app: TestAppContext) { + // #[derive(Default)] + // struct View { + // count: usize, + // } - impl super::View for View { - fn render<'a>(&self, _: &AppContext) -> ElementBox { - Empty::new().boxed() - } + // impl Entity for View { + // type Event = (); + // } - fn ui_name() -> &'static str { - "View" - } - } + // impl super::View for View { + // fn render<'a>(&self, _: &AppContext) -> ElementBox { + // Empty::new().boxed() + // } - let handle = app.add_window(|_| View::default()).1; - handle - .update(&mut app, |_, c| { - c.spawn(async { 7 }, |me, output, _| { - me.count = output; - }) - }) - .await; - app.read(|ctx| assert_eq!(handle.read(ctx).count, 7)); - handle - .update(&mut app, |_, c| { - c.spawn(async { 14 }, |me, output, _| { - me.count = output; - }) - }) - .await; - app.read(|ctx| assert_eq!(handle.read(ctx).count, 14)); - } + // fn ui_name() -> &'static str { + // "View" + // } + // } - #[crate::test(self)] - async fn test_spawn_stream_local_from_view(mut app: TestAppContext) { - #[derive(Default)] - struct View { - events: Vec>, - } + // let handle = app.add_window(|_| View::default()).1; + // handle + // .update(&mut app, |_, c| { + // c.spawn(async { 7 }, |me, output, _| { + // me.count = output; + // }) + // }) + // .await; + // app.read(|ctx| assert_eq!(handle.read(ctx).count, 7)); + // handle + // .update(&mut app, |_, c| { + // c.spawn(async { 14 }, |me, output, _| { + // me.count = output; + // }) + // }) + // .await; + // app.read(|ctx| assert_eq!(handle.read(ctx).count, 14)); + // } - impl Entity for View { - type Event = (); - } + // #[crate::test(self)] + // async fn test_spawn_stream_local_from_view(mut app: TestAppContext) { + // #[derive(Default)] + // struct View { + // events: Vec>, + // } - impl super::View for View { - fn render<'a>(&self, _: &AppContext) -> ElementBox { - Empty::new().boxed() - } + // impl Entity for View { + // type Event = (); + // } - fn ui_name() -> &'static str { - "View" - } - } + // impl super::View for View { + // fn render<'a>(&self, _: &AppContext) -> ElementBox { + // Empty::new().boxed() + // } - let (_, handle) = app.add_window(|_| View::default()); - handle - .update(&mut app, |_, c| { - c.spawn_stream( - smol::stream::iter(vec![1_usize, 2, 3]), - |me, output, _| { - me.events.push(Some(output)); - }, - |me, _| { - me.events.push(None); - }, - ) - }) - .await; + // fn ui_name() -> &'static str { + // "View" + // } + // } - app.read(|ctx| assert_eq!(handle.read(ctx).events, [Some(1), Some(2), Some(3), None])) - } + // let (_, handle) = app.add_window(|_| View::default()); + // handle + // .update(&mut app, |_, c| { + // c.spawn_stream( + // smol::stream::iter(vec![1_usize, 2, 3]), + // |me, output, _| { + // me.events.push(Some(output)); + // }, + // |me, _| { + // me.events.push(None); + // }, + // ) + // }) + // .await; + + // app.read(|ctx| assert_eq!(handle.read(ctx).events, [Some(1), Some(2), Some(3), None])) + // } #[crate::test(self)] fn test_dispatch_action(app: &mut MutableAppContext) { diff --git a/zed/src/editor/buffer/mod.rs b/zed/src/editor/buffer/mod.rs index 058fb038067bb7ecbeba1faf82a6c27a2558e985..5d039fdc99d1e8030fe713be814fe9bb13597e04 100644 --- a/zed/src/editor/buffer/mod.rs +++ b/zed/src/editor/buffer/mod.rs @@ -4,11 +4,9 @@ mod selection; mod text; pub use anchor::*; -use futures_core::future::LocalBoxFuture; pub use point::*; use seahash::SeaHasher; pub use selection::*; -use smol::future::FutureExt; pub use text::*; use crate::{ @@ -19,7 +17,7 @@ use crate::{ worktree::FileHandle, }; use anyhow::{anyhow, Result}; -use gpui::{Entity, ModelContext}; +use gpui::{Entity, ModelContext, Task}; use lazy_static::lazy_static; use rand::prelude::*; use std::{ @@ -475,21 +473,23 @@ impl Buffer { &mut self, new_file: Option, ctx: &mut ModelContext, - ) -> LocalBoxFuture<'static, Result<()>> { + ) -> Task> { let snapshot = self.snapshot(); let version = self.version.clone(); - if let Some(file) = new_file.as_ref().or(self.file.as_ref()) { - let save_task = file.save(snapshot, ctx.as_ref()); - ctx.spawn(save_task, |me, save_result, ctx| { - if save_result.is_ok() { - me.did_save(version, new_file, ctx); + let file = self.file.clone(); + let handle = ctx.handle(); + + ctx.spawn(|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() { + handle.update(&mut ctx, |me, ctx| me.did_save(version, new_file, ctx)); } - save_result - }) - .boxed_local() - } else { - async { Ok(()) }.boxed_local() - } + result + } else { + Ok(()) + } + }) } fn did_save( diff --git a/zed/src/editor/buffer_view.rs b/zed/src/editor/buffer_view.rs index 2e54336e681a5deaf59e21d7dea78990b5251994..cc22d8dcc8139fbc7b24da6859025ff0334b26cb 100644 --- a/zed/src/editor/buffer_view.rs +++ b/zed/src/editor/buffer_view.rs @@ -4,11 +4,10 @@ use super::{ }; use crate::{settings::Settings, util::post_inc, workspace, worktree::FileHandle}; use anyhow::Result; -use futures_core::future::LocalBoxFuture; use gpui::{ fonts::Properties as FontProperties, geometry::vector::Vector2F, keymap::Binding, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle, - MutableAppContext, TextLayoutCache, View, ViewContext, WeakViewHandle, + MutableAppContext, Task, TextLayoutCache, View, ViewContext, WeakViewHandle, }; use parking_lot::Mutex; use postage::watch; @@ -2348,13 +2347,13 @@ impl BufferView { ctx.notify(); let epoch = self.next_blink_epoch(); - ctx.spawn( - async move { - Timer::after(CURSOR_BLINK_INTERVAL).await; - epoch - }, - Self::resume_cursor_blinking, - ) + let handle = ctx.handle(); + ctx.spawn(|mut ctx| async move { + Timer::after(CURSOR_BLINK_INTERVAL).await; + handle.update(&mut ctx, |this, ctx| { + this.resume_cursor_blinking(epoch, ctx); + }) + }) .detach(); } @@ -2371,13 +2370,11 @@ impl BufferView { ctx.notify(); let epoch = self.next_blink_epoch(); - ctx.spawn( - async move { - Timer::after(CURSOR_BLINK_INTERVAL).await; - epoch - }, - Self::blink_cursors, - ) + let handle = ctx.handle(); + ctx.spawn(|mut ctx| async move { + Timer::after(CURSOR_BLINK_INTERVAL).await; + handle.update(&mut ctx, |this, ctx| this.blink_cursors(epoch, ctx)); + }) .detach(); } } @@ -2498,7 +2495,7 @@ impl workspace::ItemView for BufferView { &mut self, new_file: Option, ctx: &mut ViewContext, - ) -> LocalBoxFuture<'static, Result<()>> { + ) -> Task> { self.buffer.update(ctx, |b, ctx| b.save(new_file, ctx)) } diff --git a/zed/src/file_finder.rs b/zed/src/file_finder.rs index e2427adddd3313975d5a31bb58e78becad2dbdf9..0dc96e52fc59e1ab6c1f78c99da35be220ad3c8a 100644 --- a/zed/src/file_finder.rs +++ b/zed/src/file_finder.rs @@ -399,7 +399,7 @@ impl FileFinder { self.cancel_flag.store(true, atomic::Ordering::Relaxed); self.cancel_flag = Arc::new(AtomicBool::new(false)); let cancel_flag = self.cancel_flag.clone(); - let task = ctx.background_executor().spawn(async move { + let background_task = ctx.background_executor().spawn(async move { let include_root_name = snapshots.len() > 1; let matches = match_paths( snapshots.iter(), @@ -415,7 +415,12 @@ impl FileFinder { (search_id, did_cancel, query, matches) }); - ctx.spawn(task, Self::update_matches).detach(); + let handle = ctx.handle(); + ctx.spawn(|mut ctx| async move { + let matches = background_task.await; + handle.update(&mut ctx, |this, ctx| this.update_matches(matches, ctx)); + }) + .detach(); Some(()) } diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index 90f22eaba93c2f6d8328323a40f19973cf7c0cd6..81e5b345ae5b4d86d827623b2f369984b3b35569 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -6,10 +6,10 @@ use crate::{ time::ReplicaId, worktree::{FileHandle, Worktree, WorktreeHandle}, }; -use futures_core::{future::LocalBoxFuture, Future}; +use futures_core::Future; use gpui::{ color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, - ClipboardItem, Entity, EntityTask, ModelHandle, MutableAppContext, PathPromptOptions, View, + ClipboardItem, Entity, ModelHandle, MutableAppContext, PathPromptOptions, Task, View, ViewContext, ViewHandle, WeakModelHandle, }; use log::error; @@ -123,7 +123,7 @@ pub trait ItemView: View { &mut self, _: Option, _: &mut ViewContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>>; + ) -> Task>; fn should_activate_item_on_event(_: &Self::Event) -> bool { false } @@ -161,7 +161,7 @@ pub trait ItemViewHandle: Send + Sync { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>>; + ) -> Task>; } impl ItemHandle for ModelHandle { @@ -239,7 +239,7 @@ impl ItemViewHandle for ViewHandle { &self, file: Option, ctx: &mut MutableAppContext, - ) -> LocalBoxFuture<'static, anyhow::Result<()>> { + ) -> Task> { self.update(ctx, |item, ctx| item.save(file, ctx)) } @@ -359,16 +359,18 @@ impl Workspace { .cloned() .zip(entries.into_iter()) .map(|(abs_path, file)| { - ctx.spawn( - bg.spawn(async move { abs_path.is_file() }), - move |me, is_file, ctx| { + let handle = ctx.handle(); + let is_file = bg.spawn(async move { abs_path.is_file() }); + ctx.spawn(|mut ctx| async move { + let is_file = is_file.await; + handle.update(&mut ctx, |me, ctx| { if is_file { me.open_entry(file.entry_id(), ctx) } else { None } - }, - ) + }) + }) }) .collect::>(); async move { @@ -442,7 +444,7 @@ impl Workspace { &mut self, entry: (usize, Arc), ctx: &mut ViewContext, - ) -> Option> { + ) -> Option> { // If the active pane contains a view for this file, then activate // that item view. if self @@ -496,28 +498,31 @@ impl Workspace { let history = ctx .background_executor() .spawn(file.load_history(ctx.as_ref())); - ctx.spawn(history, move |_, history, ctx| { - *tx.borrow_mut() = Some(match history { - Ok(history) => Ok(Box::new(ctx.add_model(|ctx| { - Buffer::from_history(replica_id, history, Some(file), ctx) - }))), - Err(error) => Err(Arc::new(error)), + + ctx.as_mut() + .spawn(|mut ctx| async move { + *tx.borrow_mut() = Some(match history.await { + Ok(history) => Ok(Box::new(ctx.add_model(|ctx| { + Buffer::from_history(replica_id, history, Some(file), ctx) + }))), + Err(error) => Err(Arc::new(error)), + }) }) - }) - .detach() + .detach(); } let mut watch = self.loading_items.get(&entry).unwrap().clone(); - Some(ctx.spawn( - async move { - loop { - if let Some(load_result) = watch.borrow().as_ref() { - return load_result.clone(); - } - watch.next().await; + + let handle = ctx.handle(); + Some(ctx.spawn(|mut ctx| async move { + let load_result = loop { + if let Some(load_result) = watch.borrow().as_ref() { + break load_result.clone(); } - }, - move |me, load_result, ctx| { + watch.next().await; + }; + + handle.update(&mut ctx, |me, ctx| { me.loading_items.remove(&entry); match load_result { Ok(item) => { @@ -532,8 +537,8 @@ impl Workspace { log::error!("error opening item: {}", error); } } - }, - )) + }) + })) } pub fn active_item(&self, ctx: &ViewContext) -> Option> { @@ -552,28 +557,27 @@ impl Workspace { .to_path_buf(); ctx.prompt_for_new_path(&start_path, move |path, ctx| { if let Some(path) = path { - handle.update(ctx, move |this, ctx| { - let file = this.file_for_path(&path, ctx); - let task = item.save(Some(file), ctx.as_mut()); - ctx.spawn(task, move |_, result, _| { - if let Err(e) = result { - error!("failed to save item: {:?}, ", e); - } - }) - .detach() + ctx.spawn(|mut ctx| async move { + let file = + handle.update(&mut ctx, |me, ctx| me.file_for_path(&path, ctx)); + if let Err(error) = ctx.update(|ctx| item.save(Some(file), ctx)).await { + error!("failed to save item: {:?}, ", error); + } }) + .detach() } }); return; } - let task = item.save(None, ctx.as_mut()); - ctx.spawn(task, |_, result, _| { - if let Err(e) = result { - error!("failed to save item: {:?}, ", e); - } - }) - .detach() + let save = item.save(None, ctx.as_mut()); + ctx.foreground() + .spawn(async move { + if let Err(e) = save.await { + error!("failed to save item: {:?}, ", e); + } + }) + .detach(); } } diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 30383f5a0a85a1e3e4d46d0ca5f06fcae4367b0c..55d8f91b992507cbb0e19e73cd79b30e9c6bd863 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -16,7 +16,7 @@ use postage::{ prelude::{Sink, Stream}, watch, }; -use smol::{channel::Sender, Timer}; +use smol::channel::Sender; use std::{ cmp, collections::{HashMap, HashSet}, @@ -98,8 +98,24 @@ impl Worktree { scanner.run(event_stream) }); - ctx.spawn_stream(scan_state_rx, Self::observe_scan_state, |_, _| {}) - .detach(); + 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 + } + }); + + if !alive { + break; + } + } + }) + .detach(); tree } @@ -116,15 +132,17 @@ impl Worktree { pub fn next_scan_complete(&self, ctx: &mut ModelContext) -> impl Future { let scan_id = self.snapshot.scan_id; - ctx.spawn_stream( - self.scan_state.1.clone(), - move |this, scan_state, ctx| { - if matches!(scan_state, ScanState::Idle) && this.snapshot.scan_id > scan_id { - ctx.halt_stream(); + let mut scan_state = self.scan_state.1.clone(); + let handle = ctx.handle(); + ctx.spawn(|ctx| async move { + while let Some(scan_state) = scan_state.recv().await { + if handle.read_with(&ctx, |this, _| { + matches!(scan_state, ScanState::Idle) && this.snapshot.scan_id > scan_id + }) { + break; } - }, - |_, _| {}, - ) + } + }) } fn observe_scan_state(&mut self, scan_state: ScanState, ctx: &mut ModelContext) { @@ -137,9 +155,12 @@ impl Worktree { ctx.notify(); if self.is_scanning() && !self.poll_scheduled { - ctx.spawn(Timer::after(Duration::from_millis(100)), |this, _, ctx| { - this.poll_scheduled = false; - this.poll_entries(ctx); + let handle = ctx.handle(); + ctx.spawn(|mut ctx| async move { + handle.update(&mut ctx, |this, ctx| { + this.poll_scheduled = false; + this.poll_entries(ctx); + }) }) .detach(); self.poll_scheduled = true;