Merge branch 'zed2' of github.com:zed-industries/zed into zed2

Marshall Bowers created

Change summary

Cargo.lock                            |  56 +++
Cargo.toml                            |   2 
crates/gpui2/src/app.rs               |  13 
crates/gpui2/src/app/model_context.rs |  12 
crates/gpui2/src/subscription.rs      |   4 
crates/gpui2/src/window.rs            |  11 
crates/node_runtime/Cargo.toml        |   1 
crates/project2/Cargo.toml            |   2 
crates/project2/src/lsp_command.rs    |  62 ++--
crates/project2/src/project2.rs       | 400 +++++++++++++++-------------
crates/project2/src/terminals.rs      |  18 
crates/project2/src/worktree.rs       |  19 
crates/zed2/src/main.rs               |   2 
13 files changed, 351 insertions(+), 251 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -4973,7 +4973,6 @@ dependencies = [
  "async-tar",
  "async-trait",
  "futures 0.3.28",
- "gpui",
  "log",
  "parking_lot 0.11.2",
  "serde",
@@ -5973,6 +5972,61 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "project2"
+version = "0.1.0"
+dependencies = [
+ "aho-corasick",
+ "anyhow",
+ "async-trait",
+ "backtrace",
+ "client2",
+ "clock",
+ "collections",
+ "copilot2",
+ "ctor",
+ "db2",
+ "env_logger 0.9.3",
+ "fs",
+ "fsevent",
+ "futures 0.3.28",
+ "fuzzy2",
+ "git",
+ "git2",
+ "globset",
+ "gpui2",
+ "ignore",
+ "itertools 0.10.5",
+ "language2",
+ "lazy_static",
+ "log",
+ "lsp2",
+ "node_runtime",
+ "parking_lot 0.11.2",
+ "postage",
+ "prettier2",
+ "pretty_assertions",
+ "rand 0.8.5",
+ "regex",
+ "rpc",
+ "schemars",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "settings2",
+ "sha2 0.10.7",
+ "similar",
+ "smol",
+ "sum_tree",
+ "tempdir",
+ "terminal2",
+ "text",
+ "thiserror",
+ "toml 0.5.11",
+ "unindent",
+ "util",
+]
+
 [[package]]
 name = "project_panel"
 version = "0.1.0"

Cargo.toml 🔗

@@ -63,7 +63,7 @@ members = [
     "crates/prettier",
     "crates/prettier2",
     "crates/project",
-    # "crates/project2",
+    "crates/project2",
     "crates/project_panel",
     "crates/project_symbols",
     "crates/recent_projects",

crates/gpui2/src/app.rs 🔗

@@ -166,10 +166,11 @@ impl App {
 
 type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
 type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
-type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
-type Listener = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
-type QuitHandler = Box<dyn Fn(&mut AppContext) -> BoxFuture<'static, ()> + Send + Sync + 'static>;
-type ReleaseListener = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
+type Handler = Box<dyn FnMut(&mut AppContext) -> bool + Send + Sync + 'static>;
+type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
+type QuitHandler =
+    Box<dyn FnMut(&mut AppContext) -> BoxFuture<'static, ()> + Send + Sync + 'static>;
+type ReleaseListener = Box<dyn FnMut(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
 
 pub struct AppContext {
     this: Weak<Mutex<AppContext>>,
@@ -359,7 +360,7 @@ impl AppContext {
             for (entity_id, mut entity) in dropped {
                 self.observers.remove(&entity_id);
                 self.event_listeners.remove(&entity_id);
-                for release_callback in self.release_listeners.remove(&entity_id) {
+                for mut release_callback in self.release_listeners.remove(&entity_id) {
                     release_callback(&mut entity, self);
                 }
             }
@@ -579,7 +580,7 @@ impl AppContext {
 
     pub fn observe_global<G: 'static>(
         &mut self,
-        f: impl Fn(&mut Self) + Send + Sync + 'static,
+        mut f: impl FnMut(&mut Self) + Send + Sync + 'static,
     ) -> Subscription {
         self.global_observers.insert(
             TypeId::of::<G>(),

crates/gpui2/src/app/model_context.rs 🔗

@@ -43,7 +43,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     pub fn observe<T2: 'static>(
         &mut self,
         handle: &Handle<T2>,
-        on_notify: impl Fn(&mut T, Handle<T2>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
+        mut on_notify: impl FnMut(&mut T, Handle<T2>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
     ) -> Subscription
     where
         T: Any + Send + Sync,
@@ -66,7 +66,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     pub fn subscribe<E: 'static + EventEmitter>(
         &mut self,
         handle: &Handle<E>,
-        on_event: impl Fn(&mut T, Handle<E>, &E::Event, &mut ModelContext<'_, T>)
+        mut on_event: impl FnMut(&mut T, Handle<E>, &E::Event, &mut ModelContext<'_, T>)
             + Send
             + Sync
             + 'static,
@@ -92,7 +92,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
 
     pub fn on_release(
         &mut self,
-        on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static,
+        mut on_release: impl FnMut(&mut T, &mut AppContext) + Send + Sync + 'static,
     ) -> Subscription
     where
         T: 'static,
@@ -109,7 +109,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     pub fn observe_release<E: 'static>(
         &mut self,
         handle: &Handle<E>,
-        on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
+        mut on_release: impl FnMut(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static,
     ) -> Subscription
     where
         T: Any + Send + Sync,
@@ -128,7 +128,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
 
     pub fn observe_global<G: 'static>(
         &mut self,
-        f: impl Fn(&mut T, &mut ModelContext<'_, T>) + Send + Sync + 'static,
+        mut f: impl FnMut(&mut T, &mut ModelContext<'_, T>) + Send + Sync + 'static,
     ) -> Subscription
     where
         T: Any + Send + Sync,
@@ -142,7 +142,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
 
     pub fn on_app_quit<Fut>(
         &mut self,
-        on_quit: impl Fn(&mut T, &mut ModelContext<T>) -> Fut + Send + Sync + 'static,
+        mut on_quit: impl FnMut(&mut T, &mut ModelContext<T>) -> Fut + Send + Sync + 'static,
     ) -> Subscription
     where
         Fut: 'static + Future<Output = ()> + Send,

crates/gpui2/src/subscription.rs 🔗

@@ -21,7 +21,7 @@ struct SubscriberSetState<EmitterKey, Callback> {
 
 impl<EmitterKey, Callback> SubscriberSet<EmitterKey, Callback>
 where
-    EmitterKey: 'static + Ord + Clone + Debug,
+    EmitterKey: 'static + Send + Sync + Ord + Clone + Debug,
     Callback: 'static + Send + Sync,
 {
     pub fn new() -> Self {
@@ -96,7 +96,7 @@ where
 
 #[must_use]
 pub struct Subscription {
-    unsubscribe: Option<Box<dyn FnOnce()>>,
+    unsubscribe: Option<Box<dyn FnOnce() + Send + Sync + 'static>>,
 }
 
 impl Subscription {

crates/gpui2/src/window.rs 🔗

@@ -1418,7 +1418,10 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
     pub fn observe<E>(
         &mut self,
         handle: &Handle<E>,
-        on_notify: impl Fn(&mut V, Handle<E>, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
+        mut on_notify: impl FnMut(&mut V, Handle<E>, &mut ViewContext<'_, '_, V>)
+            + Send
+            + Sync
+            + 'static,
     ) -> Subscription
     where
         E: 'static,
@@ -1446,7 +1449,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
     pub fn subscribe<E: EventEmitter>(
         &mut self,
         handle: &Handle<E>,
-        on_event: impl Fn(&mut V, Handle<E>, &E::Event, &mut ViewContext<'_, '_, V>)
+        mut on_event: impl FnMut(&mut V, Handle<E>, &E::Event, &mut ViewContext<'_, '_, V>)
             + Send
             + Sync
             + 'static,
@@ -1473,7 +1476,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
 
     pub fn on_release(
         &mut self,
-        on_release: impl Fn(&mut V, &mut WindowContext) + Send + Sync + 'static,
+        mut on_release: impl FnMut(&mut V, &mut WindowContext) + Send + Sync + 'static,
     ) -> Subscription {
         let window_handle = self.window.handle;
         self.app.release_listeners.insert(
@@ -1489,7 +1492,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
     pub fn observe_release<T: 'static>(
         &mut self,
         handle: &Handle<T>,
-        on_release: impl Fn(&mut V, &mut T, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
+        mut on_release: impl FnMut(&mut V, &mut T, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
     ) -> Subscription
     where
         V: Any + Send + Sync,

crates/node_runtime/Cargo.toml 🔗

@@ -9,7 +9,6 @@ path = "src/node_runtime.rs"
 doctest = false
 
 [dependencies]
-gpui = { path = "../gpui" }
 util = { path = "../util" }
 async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
 async-tar = "0.4.2"

crates/project2/Cargo.toml 🔗

@@ -37,7 +37,7 @@ prettier2 = { path = "../prettier2" }
 rpc = { path = "../rpc" }
 settings2 = { path = "../settings2" }
 sum_tree = { path = "../sum_tree" }
-terminal = { path = "../terminal" }
+terminal2 = { path = "../terminal2" }
 util = { path = "../util" }
 
 aho-corasick = "1.1"

crates/project2/src/lsp_command.rs 🔗

@@ -32,8 +32,8 @@ pub fn lsp_formatting_options(tab_size: u32) -> lsp2::FormattingOptions {
     }
 }
 
-#[async_trait(?Send)]
-pub(crate) trait LspCommand: 'static + Sized {
+#[async_trait]
+pub(crate) trait LspCommand: 'static + Sized + Send {
     type Response: 'static + Default + Send;
     type LspRequest: 'static + Send + lsp2::request::Request;
     type ProtoRequest: 'static + Send + proto::RequestMessage;
@@ -148,7 +148,7 @@ impl From<lsp2::FormattingOptions> for FormattingOptions {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for PrepareRename {
     type Response = Option<Range<Anchor>>;
     type LspRequest = lsp2::request::PrepareRenameRequest;
@@ -183,7 +183,7 @@ impl LspCommand for PrepareRename {
         _: Handle<Project>,
         buffer: Handle<Buffer>,
         _: LanguageServerId,
-        cx: AsyncAppContext,
+        mut cx: AsyncAppContext,
     ) -> Result<Option<Range<Anchor>>> {
         buffer.update(&mut cx, |buffer, _| {
             if let Some(
@@ -279,7 +279,7 @@ impl LspCommand for PrepareRename {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for PerformRename {
     type Response = ProjectTransaction;
     type LspRequest = lsp2::request::Rename;
@@ -398,7 +398,7 @@ impl LspCommand for PerformRename {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetDefinition {
     type Response = Vec<LocationLink>;
     type LspRequest = lsp2::request::GotoDefinition;
@@ -491,7 +491,7 @@ impl LspCommand for GetDefinition {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetTypeDefinition {
     type Response = Vec<LocationLink>;
     type LspRequest = lsp2::request::GotoTypeDefinition;
@@ -783,7 +783,7 @@ fn location_links_to_proto(
         .collect()
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetReferences {
     type Response = Vec<Location>;
     type LspRequest = lsp2::request::References;
@@ -836,17 +836,19 @@ impl LspCommand for GetReferences {
                     })?
                     .await?;
 
-                target_buffer_handle.update(&mut cx, |target_buffer, cx| {
-                    let target_start = target_buffer
-                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
-                    let target_end = target_buffer
-                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
-                    references.push(Location {
-                        buffer: target_buffer_handle,
-                        range: target_buffer.anchor_after(target_start)
-                            ..target_buffer.anchor_before(target_end),
-                    });
-                })?;
+                target_buffer_handle
+                    .clone()
+                    .update(&mut cx, |target_buffer, _| {
+                        let target_start = target_buffer
+                            .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
+                        let target_end = target_buffer
+                            .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
+                        references.push(Location {
+                            buffer: target_buffer_handle,
+                            range: target_buffer.anchor_after(target_start)
+                                ..target_buffer.anchor_before(target_end),
+                        });
+                    })?;
             }
         }
 
@@ -943,7 +945,7 @@ impl LspCommand for GetReferences {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetDocumentHighlights {
     type Response = Vec<DocumentHighlight>;
     type LspRequest = lsp2::request::DocumentHighlightRequest;
@@ -978,7 +980,7 @@ impl LspCommand for GetDocumentHighlights {
         _: Handle<Project>,
         buffer: Handle<Buffer>,
         _: LanguageServerId,
-        cx: AsyncAppContext,
+        mut cx: AsyncAppContext,
     ) -> Result<Vec<DocumentHighlight>> {
         buffer.update(&mut cx, |buffer, _| {
             let mut lsp_highlights = lsp_highlights.unwrap_or_default();
@@ -1094,7 +1096,7 @@ impl LspCommand for GetDocumentHighlights {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetHover {
     type Response = Option<Hover>;
     type LspRequest = lsp2::request::HoverRequest;
@@ -1130,7 +1132,7 @@ impl LspCommand for GetHover {
             return Ok(None);
         };
 
-        let (language, range) = buffer.update(&mut cx, |buffer, cx| {
+        let (language, range) = buffer.update(&mut cx, |buffer, _| {
             (
                 buffer.language().cloned(),
                 hover.range.map(|range| {
@@ -1272,7 +1274,7 @@ impl LspCommand for GetHover {
         message: proto::GetHoverResponse,
         _: Handle<Project>,
         buffer: Handle<Buffer>,
-        cx: AsyncAppContext,
+        mut cx: AsyncAppContext,
     ) -> Result<Self::Response> {
         let contents: Vec<_> = message
             .contents
@@ -1312,7 +1314,7 @@ impl LspCommand for GetHover {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetCompletions {
     type Response = Vec<Completion>;
     type LspRequest = lsp2::request::Completion;
@@ -1342,7 +1344,7 @@ impl LspCommand for GetCompletions {
         _: Handle<Project>,
         buffer: Handle<Buffer>,
         server_id: LanguageServerId,
-        cx: AsyncAppContext,
+        mut cx: AsyncAppContext,
     ) -> Result<Vec<Completion>> {
         let mut response_list = None;
         let completions = if let Some(completions) = completions {
@@ -1543,7 +1545,7 @@ impl LspCommand for GetCompletions {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for GetCodeActions {
     type Response = Vec<CodeAction>;
     type LspRequest = lsp2::request::CodeActionRequest;
@@ -1682,7 +1684,7 @@ impl LspCommand for GetCodeActions {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for OnTypeFormatting {
     type Response = Option<Transaction>;
     type LspRequest = lsp2::request::OnTypeFormatting;
@@ -2190,7 +2192,7 @@ impl InlayHints {
     }
 }
 
-#[async_trait(?Send)]
+#[async_trait]
 impl LspCommand for InlayHints {
     type Response = Vec<InlayHint>;
     type LspRequest = lsp2::InlayHintRequest;
@@ -2253,7 +2255,7 @@ impl LspCommand for InlayHints {
             };
 
             let buffer = buffer.clone();
-            cx.spawn(|mut cx| async move {
+            cx.spawn(move |mut cx| async move {
                 InlayHints::lsp_to_project_hint(
                     lsp_hint,
                     &buffer,

crates/project2/src/project2.rs 🔗

@@ -154,7 +154,7 @@ pub struct Project {
     git_diff_debouncer: DelayedDebounced,
     nonce: u128,
     _maintain_buffer_languages: Task<()>,
-    _maintain_workspace_config: Task<()>,
+    _maintain_workspace_config: Task<Result<()>>,
     terminals: Terminals,
     copilot_lsp_subscription: Option<gpui2::Subscription>,
     copilot_log_subscription: Option<lsp2::Subscription>,
@@ -196,7 +196,7 @@ impl DelayedDebounced {
         self.cancel_channel = Some(sender);
 
         let previous_task = self.task.take();
-        self.task = Some(cx.spawn(|project, mut cx| async move {
+        self.task = Some(cx.spawn(move |project, mut cx| async move {
             let mut timer = cx.executor().timer(delay).fuse();
             if let Some(previous_task) = previous_task {
                 previous_task.await;
@@ -623,9 +623,9 @@ impl Project {
         fs: Arc<dyn Fs>,
         cx: &mut AppContext,
     ) -> Handle<Self> {
-        cx.add_model(|cx: &mut ModelContext<Self>| {
+        cx.entity(|cx: &mut ModelContext<Self>| {
             let (tx, rx) = mpsc::unbounded();
-            cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
+            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
                 .detach();
             let copilot_lsp_subscription =
                 Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx));
@@ -708,7 +708,7 @@ impl Project {
             }
 
             let (tx, rx) = mpsc::unbounded();
-            cx.spawn(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
+            cx.spawn(move |this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
                 .detach();
             let copilot_lsp_subscription =
                 Copilot::global(cx).map(|copilot| subscribe_for_copilot_events(&copilot, cx));
@@ -783,7 +783,7 @@ impl Project {
                 let _ = this.add_worktree(&worktree, cx);
             }
             this
-        });
+        })?;
         let subscription = subscription.set_model(&this, &mut cx);
 
         let user_ids = response
@@ -800,7 +800,7 @@ impl Project {
             this.set_collaborators_from_proto(response.payload.collaborators, cx)?;
             this.client_subscriptions.push(subscription);
             anyhow::Ok(())
-        })?;
+        })??;
 
         Ok(this)
     }
@@ -822,7 +822,7 @@ impl Project {
 
     fn shutdown_language_servers(
         &mut self,
-        cx: &mut ModelContext<Self>,
+        _cx: &mut ModelContext<Self>,
     ) -> impl Future<Output = ()> {
         let shutdown_futures = self
             .language_servers
@@ -988,7 +988,7 @@ impl Project {
         cx.notify();
     }
 
-    pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option<Handle<Buffer>> {
+    pub fn buffer_for_id(&self, remote_id: u64) -> Option<Handle<Buffer>> {
         self.opened_buffers
             .get(&remote_id)
             .and_then(|buffer| buffer.upgrade())
@@ -1006,7 +1006,7 @@ impl Project {
         self.user_store.clone()
     }
 
-    pub fn opened_buffers(&self, cx: &AppContext) -> Vec<Handle<Buffer>> {
+    pub fn opened_buffers(&self) -> Vec<Handle<Buffer>> {
         self.opened_buffers
             .values()
             .filter_map(|b| b.upgrade())
@@ -1068,10 +1068,7 @@ impl Project {
     }
 
     /// Collect all worktrees, including ones that don't appear in the project panel
-    pub fn worktrees<'a>(
-        &'a self,
-        cx: &'a AppContext,
-    ) -> impl 'a + DoubleEndedIterator<Item = Handle<Worktree>> {
+    pub fn worktrees<'a>(&'a self) -> impl 'a + DoubleEndedIterator<Item = Handle<Worktree>> {
         self.worktrees
             .iter()
             .filter_map(move |worktree| worktree.upgrade())
@@ -1099,7 +1096,7 @@ impl Project {
     }
 
     pub fn worktree_for_id(&self, id: WorktreeId, cx: &AppContext) -> Option<Handle<Worktree>> {
-        self.worktrees(cx)
+        self.worktrees()
             .find(|worktree| worktree.read(cx).id() == id)
     }
 
@@ -1108,7 +1105,7 @@ impl Project {
         entry_id: ProjectEntryId,
         cx: &AppContext,
     ) -> Option<Handle<Worktree>> {
-        self.worktrees(cx)
+        self.worktrees()
             .find(|worktree| worktree.read(cx).contains_entry(entry_id))
     }
 
@@ -1126,7 +1123,7 @@ impl Project {
     }
 
     pub fn contains_path(&self, path: &Path, cx: &AppContext) -> bool {
-        for worktree in self.worktrees(cx) {
+        for worktree in self.worktrees() {
             let worktree = worktree.read(cx).as_local();
             if worktree.map_or(false, |w| w.contains_abs_path(path)) {
                 return true;
@@ -1153,7 +1150,7 @@ impl Project {
         } else {
             let client = self.client.clone();
             let project_id = self.remote_id().unwrap();
-            Some(cx.spawn(|_, mut cx| async move {
+            Some(cx.spawn(move |_, mut cx| async move {
                 let response = client
                     .request(proto::CreateProjectEntry {
                         worktree_id: project_path.worktree_id.to_proto(),
@@ -1172,7 +1169,7 @@ impl Project {
                             response.worktree_scan_id as usize,
                             cx,
                         )
-                    })
+                    })?
                     .await
             }))
         }
@@ -1197,7 +1194,7 @@ impl Project {
             let client = self.client.clone();
             let project_id = self.remote_id().unwrap();
 
-            Some(cx.spawn(|_, mut cx| async move {
+            Some(cx.spawn(move |_, mut cx| async move {
                 let response = client
                     .request(proto::CopyProjectEntry {
                         project_id,
@@ -1215,7 +1212,7 @@ impl Project {
                             response.worktree_scan_id as usize,
                             cx,
                         )
-                    })
+                    })?
                     .await
             }))
         }
@@ -1240,7 +1237,7 @@ impl Project {
             let client = self.client.clone();
             let project_id = self.remote_id().unwrap();
 
-            Some(cx.spawn(|_, mut cx| async move {
+            Some(cx.spawn(move |_, mut cx| async move {
                 let response = client
                     .request(proto::RenameProjectEntry {
                         project_id,
@@ -1280,7 +1277,7 @@ impl Project {
         } else {
             let client = self.client.clone();
             let project_id = self.remote_id().unwrap();
-            Some(cx.spawn(|_, mut cx| async move {
+            Some(cx.spawn(move |_, mut cx| async move {
                 let response = client
                     .request(proto::DeleteProjectEntry {
                         project_id,
@@ -1294,7 +1291,7 @@ impl Project {
                             response.worktree_scan_id as usize,
                             cx,
                         )
-                    })
+                    })?
                     .await
             }))
         }
@@ -1317,7 +1314,7 @@ impl Project {
                 project_id: self.remote_id().unwrap(),
                 entry_id: entry_id.to_proto(),
             });
-            Some(cx.spawn(|_, mut cx| async move {
+            Some(cx.spawn(move |_, mut cx| async move {
                 let response = request.await?;
                 if let Some(worktree) = worktree.upgrade() {
                     worktree
@@ -1326,7 +1323,7 @@ impl Project {
                                 .as_remote_mut()
                                 .unwrap()
                                 .wait_for_snapshot(response.worktree_scan_id as usize)
-                        })
+                        })?
                         .await?;
                 }
                 Ok(())
@@ -1341,7 +1338,7 @@ impl Project {
         self.client_subscriptions.push(
             self.client
                 .subscribe_to_entity(project_id)?
-                .set_model(&cx.handle().upgrade(), &mut cx.to_async()),
+                .set_model(&cx.handle(), &mut cx.to_async()),
         );
 
         for open_buffer in self.opened_buffers.values_mut() {
@@ -1380,7 +1377,7 @@ impl Project {
         }
 
         let store = cx.global::<SettingsStore>();
-        for worktree in self.worktrees(cx) {
+        for worktree in self.worktrees() {
             let worktree_id = worktree.read(cx).id().to_proto();
             for (path, content) in store.local_settings(worktree.entity_id().as_u64() as usize) {
                 self.client
@@ -1403,8 +1400,8 @@ impl Project {
                 while let Some(update) = updates_rx.next().await {
                     match update {
                         LocalProjectUpdate::WorktreesChanged => {
-                            let worktrees = this.update(&mut cx, |this, cx| {
-                                this.worktrees(cx).collect::<Vec<_>>()
+                            let worktrees = this.update(&mut cx, |this, _cx| {
+                                this.worktrees().collect::<Vec<_>>()
                             })?;
                             let update_project = this
                                 .update(&mut cx, |this, cx| {
@@ -1441,9 +1438,9 @@ impl Project {
 
                             let Some(buffer) = buffer else { continue };
                             let operations =
-                                buffer.read_with(&cx, |b, cx| b.serialize_ops(None, cx));
+                                buffer.update(&mut cx, |b, cx| b.serialize_ops(None, cx))?;
                             let operations = operations.await;
-                            let state = buffer.read_with(&cx, |buffer, _| buffer.to_proto());
+                            let state = buffer.update(&mut cx, |buffer, _| buffer.to_proto())?;
 
                             let initial_state = proto::CreateBufferForPeer {
                                 project_id,
@@ -1452,7 +1449,7 @@ impl Project {
                             };
                             if client.send(initial_state).log_err().is_some() {
                                 let client = client.clone();
-                                cx.background()
+                                cx.executor()
                                     .spawn(async move {
                                         let mut chunks = split_operations(operations).peekable();
                                         while let Some(chunk) = chunks.next() {
@@ -1683,12 +1680,12 @@ impl Project {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<(ProjectEntryId, AnyHandle)>> {
         let task = self.open_buffer(path, cx);
-        cx.spawn_weak(|_, cx| async move {
+        cx.spawn(move |_, mut cx| async move {
             let buffer = task.await?;
             let project_entry_id = buffer
-                .read(&cx, |buffer, cx| {
+                .update(&mut cx, |buffer, cx| {
                     File::from_dyn(buffer.file()).and_then(|file| file.project_entry_id(cx))
-                })
+                })?
                 .ok_or_else(|| anyhow!("no project entry"))?;
 
             let buffer: &AnyHandle = &buffer;
@@ -1749,14 +1746,15 @@ impl Project {
                         this.loading_buffers_by_path.remove(&project_path);
                         let buffer = load_result.map_err(Arc::new)?;
                         Ok(buffer)
-                    }));
+                    })?);
+                    anyhow::Ok(())
                 })
                 .detach();
                 rx
             }
         };
 
-        cx.foreground().spawn(async move {
+        cx.executor().spawn(async move {
             wait_for_loading_buffer(loading_watch)
                 .await
                 .map_err(|error| anyhow!("{}", error))
@@ -1774,9 +1772,9 @@ impl Project {
             let worktree = worktree.as_local_mut().unwrap();
             worktree.load_buffer(buffer_id, path, cx)
         });
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let buffer = load_buffer.await?;
-            this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))?;
+            this.update(&mut cx, |this, cx| this.register_buffer(&buffer, cx))??;
             Ok(buffer)
         })
     }
@@ -1792,7 +1790,7 @@ impl Project {
         let remote_worktree_id = worktree.read(cx).id();
         let path = path.clone();
         let path_string = path.to_string_lossy().to_string();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let response = rpc
                 .request(proto::OpenBufferByPath {
                     project_id,
@@ -1802,7 +1800,7 @@ impl Project {
                 .await?;
             this.update(&mut cx, |this, cx| {
                 this.wait_for_remote_buffer(response.buffer_id, cx)
-            })
+            })?
             .await
         })
     }
@@ -1815,34 +1813,35 @@ impl Project {
         language_server_name: LanguageServerName,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Handle<Buffer>>> {
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let abs_path = abs_path
                 .to_file_path()
                 .map_err(|_| anyhow!("can't convert URI to path"))?;
             let (worktree, relative_path) = if let Some(result) =
-                this.read(&cx, |this, cx| this.find_local_worktree(&abs_path, cx))
+                this.update(&mut cx, |this, cx| this.find_local_worktree(&abs_path, cx))?
             {
                 result
             } else {
                 let worktree = this
                     .update(&mut cx, |this, cx| {
                         this.create_local_worktree(&abs_path, false, cx)
-                    })
+                    })?
                     .await?;
                 this.update(&mut cx, |this, cx| {
                     this.language_server_ids.insert(
                         (worktree.read(cx).id(), language_server_name),
                         language_server_id,
                     );
-                });
+                })
+                .ok();
                 (worktree, PathBuf::new())
             };
 
             let project_path = ProjectPath {
-                worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
+                worktree_id: worktree.update(&mut cx, |worktree, _| worktree.id())?,
                 path: relative_path.into(),
             };
-            this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))
+            this.update(&mut cx, |this, cx| this.open_buffer(project_path, cx))?
                 .await
         })
     }
@@ -1852,7 +1851,7 @@ impl Project {
         id: u64,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Handle<Buffer>>> {
-        if let Some(buffer) = self.buffer_for_id(id, cx) {
+        if let Some(buffer) = self.buffer_for_id(id) {
             Task::ready(Ok(buffer))
         } else if self.is_local() {
             Task::ready(Err(anyhow!("buffer {} does not exist", id)))
@@ -1860,11 +1859,11 @@ impl Project {
             let request = self
                 .client
                 .request(proto::OpenBufferById { project_id, id });
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn(move |this, mut cx| async move {
                 let buffer_id = request.await?.buffer_id;
                 this.update(&mut cx, |this, cx| {
                     this.wait_for_remote_buffer(buffer_id, cx)
-                })
+                })?
                 .await
             })
         } else {
@@ -1877,10 +1876,11 @@ impl Project {
         buffers: HashSet<Handle<Buffer>>,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<()>> {
-        cx.spawn(|this, mut cx| async move {
-            let save_tasks = buffers
-                .into_iter()
-                .map(|buffer| this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx)));
+        cx.spawn(move |this, mut cx| async move {
+            let save_tasks = buffers.into_iter().filter_map(|buffer| {
+                this.update(&mut cx, |this, cx| this.save_buffer(buffer, cx))
+                    .ok()
+            });
             try_join_all(save_tasks).await?;
             Ok(())
         })
@@ -1912,11 +1912,11 @@ impl Project {
         let old_file = File::from_dyn(buffer.read(cx).file())
             .filter(|f| f.is_local())
             .cloned();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             if let Some(old_file) = &old_file {
                 this.update(&mut cx, |this, cx| {
                     this.unregister_buffer_from_language_servers(&buffer, old_file, cx);
-                });
+                })?;
             }
             let (worktree, path) = worktree_task.await?;
             worktree
@@ -1925,13 +1925,13 @@ impl Project {
                         worktree.save_buffer(buffer.clone(), path.into(), true, cx)
                     }
                     Worktree::Remote(_) => panic!("cannot remote buffers as new files"),
-                })
+                })?
                 .await?;
 
             this.update(&mut cx, |this, cx| {
                 this.detect_language_for_buffer(&buffer, cx);
                 this.register_buffer_with_language_servers(&buffer, cx);
-            });
+            })?;
             Ok(())
         })
     }
@@ -2242,7 +2242,7 @@ impl Project {
                             is_local,
                             &mut cx,
                         )
-                        .await;
+                        .await?;
 
                         this.update(&mut cx, |this, _| {
                             if let Some(project_id) = this.remote_id() {
@@ -2254,7 +2254,7 @@ impl Project {
                                     })
                                     .log_err();
                             }
-                        });
+                        })?;
                     }
                 }
             }
@@ -2266,7 +2266,7 @@ impl Project {
                 is_local,
                 &mut cx,
             )
-            .await;
+            .await?;
         }
 
         Ok(())
@@ -2431,9 +2431,9 @@ impl Project {
                             const DISK_BASED_DIAGNOSTICS_DEBOUNCE: Duration =
                                 Duration::from_secs(1);
 
-                            let task = cx.spawn(|this, mut cx| async move {
+                            let task = cx.spawn(move |this, mut cx| async move {
                                 cx.executor().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await;
-                                if let Some(this) = this.upgrade(&cx) {
+                                if let Some(this) = this.upgrade() {
                                     this.update(&mut cx, |this, cx| {
                                         this.disk_based_diagnostics_finished(
                                             language_server_id,
@@ -2447,7 +2447,7 @@ impl Project {
                                                 },
                                             )
                                             .ok();
-                                    });
+                                    }).ok();
                                 }
                             });
                             *simulate_disk_based_diagnostics_completion = Some(task);
@@ -2521,20 +2521,18 @@ impl Project {
     }
 
     fn recalculate_buffer_diffs(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
-        cx.spawn(|this, mut cx| async move {
-            let buffers: Vec<_> = this.update(&mut cx, |this, _| {
-                this.buffers_needing_diff.drain().collect()
-            });
-
-            let tasks: Vec<_> = this.update(&mut cx, |_, cx| {
-                buffers
-                    .iter()
-                    .filter_map(|buffer| {
-                        let buffer = buffer.upgrade()?;
-                        buffer.update(cx, |buffer, cx| buffer.git_diff_recalc(cx))
-                    })
-                    .collect()
-            });
+        let buffers = self.buffers_needing_diff.drain().collect::<Vec<_>>();
+        cx.spawn(move |this, mut cx| async move {
+            let tasks: Vec<_> = buffers
+                .iter()
+                .filter_map(|buffer| {
+                    let buffer = buffer.upgrade()?;
+                    buffer
+                        .update(&mut cx, |buffer, cx| buffer.git_diff_recalc(cx))
+                        .ok()
+                        .flatten()
+                })
+                .collect();
 
             futures::future::join_all(tasks).await;
 
@@ -2549,7 +2547,8 @@ impl Project {
                         }
                     }
                 }
-            });
+            })
+            .ok();
         })
     }
 
@@ -2581,74 +2580,78 @@ impl Project {
     ) -> Task<()> {
         let mut subscription = languages.subscribe();
         let mut prev_reload_count = languages.reload_count();
-        cx.spawn_weak(|project, mut cx| async move {
+        cx.spawn(move |project, mut cx| async move {
             while let Some(()) = subscription.next().await {
-                if let Some(project) = project.upgrade(&cx) {
+                if let Some(project) = project.upgrade() {
                     // If the language registry has been reloaded, then remove and
                     // re-assign the languages on all open buffers.
                     let reload_count = languages.reload_count();
                     if reload_count > prev_reload_count {
                         prev_reload_count = reload_count;
-                        project.update(&mut cx, |this, cx| {
-                            let buffers = this
-                                .opened_buffers
-                                .values()
-                                .filter_map(|b| b.upgrade())
-                                .collect::<Vec<_>>();
-                            for buffer in buffers {
-                                if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned() {
-                                    this.unregister_buffer_from_language_servers(&buffer, &f, cx);
-                                    buffer.update(cx, |buffer, cx| buffer.set_language(None, cx));
+                        project
+                            .update(&mut cx, |this, cx| {
+                                let buffers = this
+                                    .opened_buffers
+                                    .values()
+                                    .filter_map(|b| b.upgrade())
+                                    .collect::<Vec<_>>();
+                                for buffer in buffers {
+                                    if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned()
+                                    {
+                                        this.unregister_buffer_from_language_servers(
+                                            &buffer, &f, cx,
+                                        );
+                                        buffer
+                                            .update(cx, |buffer, cx| buffer.set_language(None, cx));
+                                    }
                                 }
-                            }
-                        });
+                            })
+                            .ok();
                     }
 
-                    project.update(&mut cx, |project, cx| {
-                        let mut plain_text_buffers = Vec::new();
-                        let mut buffers_with_unknown_injections = Vec::new();
-                        for buffer in project.opened_buffers.values() {
-                            if let Some(handle) = buffer.upgrade() {
-                                let buffer = &handle.read(cx);
-                                if buffer.language().is_none()
-                                    || buffer.language() == Some(&*language2::PLAIN_TEXT)
-                                {
-                                    plain_text_buffers.push(handle);
-                                } else if buffer.contains_unknown_injections() {
-                                    buffers_with_unknown_injections.push(handle);
+                    project
+                        .update(&mut cx, |project, cx| {
+                            let mut plain_text_buffers = Vec::new();
+                            let mut buffers_with_unknown_injections = Vec::new();
+                            for buffer in project.opened_buffers.values() {
+                                if let Some(handle) = buffer.upgrade() {
+                                    let buffer = &handle.read(cx);
+                                    if buffer.language().is_none()
+                                        || buffer.language() == Some(&*language2::PLAIN_TEXT)
+                                    {
+                                        plain_text_buffers.push(handle);
+                                    } else if buffer.contains_unknown_injections() {
+                                        buffers_with_unknown_injections.push(handle);
+                                    }
                                 }
                             }
-                        }
 
-                        for buffer in plain_text_buffers {
-                            project.detect_language_for_buffer(&buffer, cx);
-                            project.register_buffer_with_language_servers(&buffer, cx);
-                        }
+                            for buffer in plain_text_buffers {
+                                project.detect_language_for_buffer(&buffer, cx);
+                                project.register_buffer_with_language_servers(&buffer, cx);
+                            }
 
-                        for buffer in buffers_with_unknown_injections {
-                            buffer.update(cx, |buffer, cx| buffer.reparse(cx));
-                        }
-                    });
+                            for buffer in buffers_with_unknown_injections {
+                                buffer.update(cx, |buffer, cx| buffer.reparse(cx));
+                            }
+                        })
+                        .ok();
                 }
             }
         })
     }
 
-    fn maintain_workspace_config(cx: &mut ModelContext<Project>) -> Task<()> {
+    fn maintain_workspace_config(cx: &mut ModelContext<Project>) -> Task<Result<()>> {
         let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
         let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx);
 
-        let settings_observation = cx.observe_global::<SettingsStore, _>(move |_, _| {
+        let settings_observation = cx.observe_global::<SettingsStore>(move |_, _| {
             *settings_changed_tx.borrow_mut() = ();
         });
 
-        cx.spawn_weak(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             while let Some(_) = settings_changed_rx.next().await {
-                let Some(this) = this.upgrade(&cx) else {
-                    break;
-                };
-
-                let servers: Vec<_> = this.read_with(&cx, |this, _| {
+                let servers: Vec<_> = this.update(&mut cx, |this, _| {
                     this.language_servers
                         .values()
                         .filter_map(|state| match state {
@@ -2658,11 +2661,11 @@ impl Project {
                             } => Some((adapter.clone(), server.clone())),
                         })
                         .collect()
-                });
+                })?;
 
                 for (adapter, server) in servers {
                     let workspace_config =
-                        cx.update(|cx| adapter.workspace_configuration(cx)).await;
+                        cx.update(|cx| adapter.workspace_configuration(cx))?.await;
                     server
                         .notify::<lsp2::notification::DidChangeConfiguration>(
                             lsp2::DidChangeConfigurationParams {
@@ -2674,6 +2677,7 @@ impl Project {
             }
 
             drop(settings_observation);
+            anyhow::Ok(())
         })
     }
 
@@ -2717,12 +2721,12 @@ impl Project {
         let task_buffer = buffer.clone();
         let prettier_installation_task =
             self.install_default_formatters(worktree, &new_language, &settings, cx);
-        cx.spawn(|project, mut cx| async move {
+        cx.spawn(move |project, mut cx| async move {
             prettier_installation_task.await?;
             let _ = project
                 .update(&mut cx, |project, cx| {
                     project.prettier_instance_for_buffer(&task_buffer, cx)
-                })
+                })?
                 .await;
             anyhow::Ok(())
         })
@@ -2806,9 +2810,9 @@ impl Project {
             let language = language.clone();
             let key = key.clone();
 
-            cx.spawn_weak(|this, mut cx| async move {
+            cx.spawn(move |this, mut cx| async move {
                 let result = Self::setup_and_insert_language_server(
-                    this,
+                    this.clone(),
                     initialization_options,
                     pending_server,
                     adapter.clone(),
@@ -2839,7 +2843,8 @@ impl Project {
                                         installation_test_binary,
                                         cx,
                                     )
-                                });
+                                })
+                                .ok();
                             }
                         }
 
@@ -2883,10 +2888,15 @@ impl Project {
             // TODO: This is race-safe with regards to preventing new instances from
             // starting while deleting, but existing instances in other projects are going
             // to be very confused and messed up
-            this.update(&mut cx, |this, cx| {
-                this.languages.delete_server_container(adapter.clone(), cx)
-            })
-            .await;
+            let Some(task) = this
+                .update(&mut cx, |this, cx| {
+                    this.languages.delete_server_container(adapter.clone(), cx)
+                })
+                .log_err()
+            else {
+                return;
+            };
+            task.await;
 
             this.update(&mut cx, |this, mut cx| {
                 let worktrees = this.worktrees.clone();
@@ -2907,6 +2917,7 @@ impl Project {
                     );
                 }
             })
+            .ok();
         }))
     }
 
@@ -2921,7 +2932,7 @@ impl Project {
         cx: &mut AsyncAppContext,
     ) -> Result<Option<Arc<LanguageServer>>> {
         let setup = Self::setup_pending_language_server(
-            this,
+            this.clone(),
             initialization_options,
             pending_server,
             adapter.clone(),
@@ -2947,7 +2958,7 @@ impl Project {
                 key,
                 cx,
             )
-        })?;
+        })??;
 
         Ok(Some(language_server))
     }
@@ -2960,7 +2971,7 @@ impl Project {
         server_id: LanguageServerId,
         cx: &mut AsyncAppContext,
     ) -> Result<Option<Arc<LanguageServer>>> {
-        let workspace_config = cx.update(|cx| adapter.workspace_configuration(cx)).await;
+        let workspace_config = cx.update(|cx| adapter.workspace_configuration(cx))?.await;
         let language_server = match pending_server.task.await? {
             Some(server) => server,
             None => return Ok(None),
@@ -2969,8 +2980,8 @@ impl Project {
         language_server
             .on_notification::<lsp2::notification::PublishDiagnostics, _>({
                 let adapter = adapter.clone();
+                let this = this.clone();
                 move |mut params, mut cx| {
-                    let this = this;
                     let adapter = adapter.clone();
                     adapter.process_diagnostics(&mut params);
                     if let Some(this) = this.upgrade() {
@@ -2982,7 +2993,8 @@ impl Project {
                                 cx,
                             )
                             .log_err();
-                        });
+                        })
+                        .ok();
                     }
                 }
             })
@@ -2991,11 +3003,11 @@ impl Project {
         language_server
             .on_request::<lsp2::request::WorkspaceConfiguration, _, _>({
                 let adapter = adapter.clone();
-                move |params, mut cx| {
+                move |params, cx| {
                     let adapter = adapter.clone();
                     async move {
                         let workspace_config =
-                            cx.update(|cx| adapter.workspace_configuration(cx)).await;
+                            cx.update(|cx| adapter.workspace_configuration(cx))?.await;
                         Ok(params
                             .items
                             .into_iter()
@@ -3019,9 +3031,11 @@ impl Project {
         // avoid stalling any language server like `gopls` which waits for a response
         // to these requests when initializing.
         language_server
-            .on_request::<lsp2::request::WorkDoneProgressCreate, _, _>(
-                move |params, mut cx| async move {
-                    if let Some(this) = this.upgrade() {
+            .on_request::<lsp2::request::WorkDoneProgressCreate, _, _>({
+                let this = this.clone();
+                move |params, mut cx| {
+                    let this = this.clone();
+                    async move {
                         this.update(&mut cx, |this, _| {
                             if let Some(status) = this.language_server_statuses.get_mut(&server_id)
                             {
@@ -3029,27 +3043,33 @@ impl Project {
                                     status.progress_tokens.insert(token);
                                 }
                             }
-                        });
+                        })?;
+
+                        Ok(())
                     }
-                    Ok(())
-                },
-            )
+                }
+            })
             .detach();
         language_server
             .on_request::<lsp2::request::RegisterCapability, _, _>({
-                move |params, mut cx| async move {
-                    let this = this.upgrade().ok_or_else(|| anyhow!("project dropped"))?;
-                    for reg in params.registrations {
-                        if reg.method == "workspace/didChangeWatchedFiles" {
-                            if let Some(options) = reg.register_options {
-                                let options = serde_json::from_value(options)?;
-                                this.update(&mut cx, |this, cx| {
-                                    this.on_lsp_did_change_watched_files(server_id, options, cx);
-                                });
+                let this = this.clone();
+                move |params, mut cx| {
+                    let this = this.clone();
+                    async move {
+                        for reg in params.registrations {
+                            if reg.method == "workspace/didChangeWatchedFiles" {
+                                if let Some(options) = reg.register_options {
+                                    let options = serde_json::from_value(options)?;
+                                    this.update(&mut cx, |this, cx| {
+                                        this.on_lsp_did_change_watched_files(
+                                            server_id, options, cx,
+                                        );
+                                    })?;
+                                }
                             }
                         }
+                        Ok(())
                     }
-                    Ok(())
                 }
             })
             .detach();
@@ -3057,24 +3077,34 @@ impl Project {
         language_server
             .on_request::<lsp2::request::ApplyWorkspaceEdit, _, _>({
                 let adapter = adapter.clone();
+                let this = this.clone();
                 move |params, cx| {
-                    Self::on_lsp_workspace_edit(this, params, server_id, adapter.clone(), cx)
+                    Self::on_lsp_workspace_edit(
+                        this.clone(),
+                        params,
+                        server_id,
+                        adapter.clone(),
+                        cx,
+                    )
                 }
             })
             .detach();
 
         language_server
             .on_request::<lsp2::request::InlayHintRefreshRequest, _, _>({
-                move |(), mut cx| async move {
-                    let this = this.upgrade().ok_or_else(|| anyhow!("project dropped"))?;
-                    this.update(&mut cx, |project, cx| {
-                        cx.emit(Event::RefreshInlayHints);
-                        project.remote_id().map(|project_id| {
-                            project.client.send(proto::RefreshInlayHints { project_id })
-                        })
-                    })
-                    .transpose()?;
-                    Ok(())
+                let this = this.clone();
+                move |(), mut cx| {
+                    let this = this.clone();
+                    async move {
+                        this.update(&mut cx, |project, cx| {
+                            cx.emit(Event::RefreshInlayHints);
+                            project.remote_id().map(|project_id| {
+                                project.client.send(proto::RefreshInlayHints { project_id })
+                            })
+                        })?
+                        .transpose()?;
+                        Ok(())
+                    }
                 }
             })
             .detach();
@@ -3092,7 +3122,8 @@ impl Project {
                             disk_based_diagnostics_progress_token.clone(),
                             cx,
                         );
-                    });
+                    })
+                    .ok();
                 }
             })
             .detach();
@@ -3282,7 +3313,7 @@ impl Project {
 
             let server_state = self.language_servers.remove(&server_id);
             cx.emit(Event::LanguageServerRemoved(server_id));
-            cx.spawn_weak(|this, mut cx| async move {
+            cx.spawn(move |this, mut cx| async move {
                 let mut root_path = None;
 
                 let server = match server_state {
@@ -3298,11 +3329,12 @@ impl Project {
                     }
                 }
 
-                if let Some(this) = this.upgrade(&cx) {
+                if let Some(this) = this.upgrade() {
                     this.update(&mut cx, |this, cx| {
                         this.language_server_statuses.remove(&server_id);
                         cx.notify();
-                    });
+                    })
+                    .ok();
                 }
 
                 (root_path, orphaned_worktrees)
@@ -3358,7 +3390,7 @@ impl Project {
         }
         let mut stops = stops.into_iter();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let (original_root_path, mut orphaned_worktrees) = stops.next().unwrap().await;
             for stop in stops {
                 let (_, worktrees) = stop.await;
@@ -3392,7 +3424,8 @@ impl Project {
                         }
                     }
                 }
-            });
+            })
+            .ok();
         })
         .detach();
     }
@@ -3412,7 +3445,7 @@ impl Project {
             return;
         }
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             log::info!("About to spawn test binary");
 
             // A lack of test binary counts as a failure
@@ -3451,9 +3484,12 @@ impl Project {
 
             if errored {
                 log::warn!("test binary check failed");
-                let task = this.update(&mut cx, move |this, mut cx| {
-                    this.reinstall_language_server(language, adapter, server_id, &mut cx)
-                });
+                let task = this
+                    .update(&mut cx, move |this, mut cx| {
+                        this.reinstall_language_server(language, adapter, server_id, &mut cx)
+                    })
+                    .ok()
+                    .flatten();
 
                 if let Some(task) = task {
                     task.await;
@@ -3718,7 +3754,7 @@ impl Project {
             .upgrade()
             .ok_or_else(|| anyhow!("project project closed"))?;
         let language_server = this
-            .read(&cx, |this, _| this.language_server_for_id(server_id))
+            .update(&mut cx, |this, _| this.language_server_for_id(server_id))?
             .ok_or_else(|| anyhow!("language server not found"))?;
         let transaction = Self::deserialize_workspace_edit(
             this.clone(),
@@ -3735,7 +3771,7 @@ impl Project {
                 this.last_workspace_edits_by_language_server
                     .insert(server_id, transaction);
             }
-        });
+        })?;
         Ok(lsp2::ApplyWorkspaceEditResponse {
             applied: true,
             failed_change: None,
@@ -4004,7 +4040,7 @@ impl Project {
         let remote_buffers = self.remote_id().zip(remote_buffers);
         let client = self.client.clone();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let mut project_transaction = ProjectTransaction::default();
 
             if let Some((project_id, remote_buffers)) = remote_buffers {

crates/project2/src/terminals.rs 🔗

@@ -1,7 +1,7 @@
 use crate::Project;
-use gpui2::{AnyWindowHandle, Handle, ModelContext, WeakHandle};
+use gpui2::{AnyWindowHandle, Context, Handle, ModelContext, WeakHandle};
 use std::path::{Path, PathBuf};
-use terminal::{
+use terminal2::{
     terminal_settings::{self, TerminalSettings, VenvSettingsContent},
     Terminal, TerminalBuilder,
 };
@@ -10,7 +10,7 @@ use terminal::{
 use std::os::unix::ffi::OsStrExt;
 
 pub struct Terminals {
-    pub(crate) local_handles: Vec<WeakHandle<terminal::Terminal>>,
+    pub(crate) local_handles: Vec<WeakHandle<terminal2::Terminal>>,
 }
 
 impl Project {
@@ -36,19 +36,23 @@ impl Project {
                 Some(settings.blinking.clone()),
                 settings.alternate_scroll,
                 window,
+                |index, cx| todo!("color_for_index"),
             )
             .map(|builder| {
-                let terminal_handle = cx.add_model(|cx| builder.subscribe(cx));
+                let terminal_handle = cx.entity(|cx| builder.subscribe(cx));
 
                 self.terminals
                     .local_handles
                     .push(terminal_handle.downgrade());
 
-                let id = terminal_handle.id();
+                let id = terminal_handle.entity_id();
                 cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
                     let handles = &mut project.terminals.local_handles;
 
-                    if let Some(index) = handles.iter().position(|terminal| terminal.id() == id) {
+                    if let Some(index) = handles
+                        .iter()
+                        .position(|terminal| terminal.entity_id() == id)
+                    {
                         handles.remove(index);
                         cx.notify();
                     }
@@ -116,7 +120,7 @@ impl Project {
         }
     }
 
-    pub fn local_terminal_handles(&self) -> &Vec<WeakHandle<terminal::Terminal>> {
+    pub fn local_terminal_handles(&self) -> &Vec<WeakHandle<terminal2::Terminal>> {
         &self.terminals.local_handles
     }
 }

crates/project2/src/worktree.rs 🔗

@@ -358,7 +358,8 @@ impl Worktree {
                             }
                         }
                         cx.notify();
-                    });
+                    })
+                    .ok();
                 }
             })
             .detach();
@@ -399,7 +400,7 @@ impl Worktree {
         })
     }
 
-    pub fn remote<C: Context>(
+    pub fn remote(
         project_remote_id: u64,
         replica_id: ReplicaId,
         worktree: proto::WorktreeMetadata,
@@ -935,7 +936,7 @@ impl LocalWorktree {
         let version = buffer.version();
         let save = self.write_file(path, text, buffer.line_ending(), cx);
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let entry = save.await?;
             let this = this.upgrade().context("worktree dropped")?;
 
@@ -962,7 +963,7 @@ impl LocalWorktree {
                     if has_changed_file {
                         buffer.file_updated(new_file, cx).detach();
                     }
-                });
+                })?;
             }
 
             if let Some(project_id) = project_id {
@@ -977,7 +978,7 @@ impl LocalWorktree {
 
             buffer_handle.update(&mut cx, |buffer, cx| {
                 buffer.did_save(version.clone(), fingerprint, entry.mtime, cx);
-            });
+            })?;
 
             Ok(())
         })
@@ -1245,7 +1246,7 @@ impl LocalWorktree {
             .unbounded_send((self.snapshot(), Arc::from([]), Arc::from([])))
             .ok();
 
-        let worktree_id = cx.entity_id().;
+        let worktree_id = cx.entity_id().as_u64();
         let _maintain_remote_snapshot = cx.executor().spawn(async move {
             let mut is_first = true;
             while let Some((snapshot, entry_changes, repo_changes)) = snapshots_rx.next().await {
@@ -1338,7 +1339,7 @@ impl RemoteWorktree {
         let version = buffer.version();
         let rpc = self.client.clone();
         let project_id = self.project_id;
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(move |_, mut cx| async move {
             let response = rpc
                 .request(proto::SaveBuffer {
                     project_id,
@@ -1446,14 +1447,14 @@ impl RemoteWorktree {
         cx: &mut ModelContext<Worktree>,
     ) -> Task<Result<()>> {
         let wait_for_snapshot = self.wait_for_snapshot(scan_id);
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             wait_for_snapshot.await?;
             this.update(&mut cx, |worktree, _| {
                 let worktree = worktree.as_remote_mut().unwrap();
                 let mut snapshot = worktree.background_snapshot.lock();
                 snapshot.delete_entry(id);
                 worktree.snapshot = snapshot.clone();
-            });
+            })?;
             Ok(())
         })
     }

crates/zed2/src/main.rs 🔗

@@ -110,7 +110,7 @@ fn main() {
         // handle_keymap_file_changes(user_keymap_file_rx, cx);
 
         // let client = client2::Client::new(http.clone(), cx);
-        let mut languages = LanguageRegistry::new(login_shell_env_loaded);
+        let languages = LanguageRegistry::new(login_shell_env_loaded);
         let copilot_language_server_id = languages.next_language_server_id();
         // languages.set_executor(cx.background().clone());
         // languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());