Fix warnings/errors now that `AsyncAppContext::update` returns `Result`

Antonio Scandurra created

Change summary

Cargo.lock                                          |   1 
crates/activity_indicator/src/activity_indicator.rs |   5 
crates/auto_update/src/auto_update.rs               |   2 
crates/collab/src/tests/integration_tests.rs        |   9 
crates/collab_ui/src/collab_ui.rs                   |   2 
crates/collab_ui/src/contact_finder.rs              |   2 
crates/command_palette/src/command_palette.rs       |   4 
crates/diagnostics/src/diagnostics.rs               |   2 
crates/editor/src/editor.rs                         |  71 +++--
crates/editor/src/hover_popover.rs                  |   4 
crates/editor/src/items.rs                          |  14 
crates/editor/src/link_go_to_definition.rs          |   2 
crates/editor/src/scroll.rs                         |  10 
crates/feedback/src/feedback_editor.rs              |  16 
crates/file_finder/src/file_finder.rs               |   5 
crates/journal/src/journal.rs                       |  58 ++--
crates/language_selector/Cargo.toml                 |   1 
crates/language_selector/src/language_selector.rs   |  37 ++-
crates/picker/src/picker.rs                         |  31 +-
crates/project_panel/src/project_panel.rs           |   6 
crates/project_symbols/src/project_symbols.rs       |   5 
crates/recent_projects/src/recent_projects.rs       |  31 +-
crates/search/src/buffer_search.rs                  |   4 
crates/terminal_view/src/terminal_view.rs           |   4 
crates/theme_selector/src/theme_selector.rs         |  22 +
crates/welcome/src/base_keymap_picker.rs            |  20 +
crates/workspace/src/item.rs                        |  41 +-
crates/workspace/src/pane.rs                        |   6 
crates/workspace/src/persistence/model.rs           |  17 
crates/workspace/src/shared_screen.rs               |   8 
crates/workspace/src/workspace.rs                   | 136 ++++++-----
crates/zed/src/main.rs                              | 130 ++++++-----
crates/zed/src/zed.rs                               | 167 +++++++++-----
33 files changed, 498 insertions(+), 375 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3366,6 +3366,7 @@ dependencies = [
  "project",
  "settings",
  "theme",
+ "util",
  "workspace",
 ]
 

crates/activity_indicator/src/activity_indicator.rs 🔗

@@ -2,7 +2,7 @@ use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage};
 use editor::Editor;
 use futures::StreamExt;
 use gpui::{
-    actions,
+    actions, anyhow,
     elements::*,
     platform::{CursorStyle, MouseButton},
     Action, AppContext, Entity, ModelHandle, View, ViewContext, ViewHandle,
@@ -73,11 +73,12 @@ impl ActivityIndicator {
                                 status: event,
                             });
                             cx.notify();
-                        });
+                        })?;
                     } else {
                         break;
                     }
                 }
+                anyhow::Ok(())
             })
             .detach();
             cx.observe(&project, |_, _, cx| cx.notify()).detach();

crates/auto_update/src/auto_update.rs 🔗

@@ -113,7 +113,7 @@ pub fn notify_of_any_new_update(
                         .read(cx)
                         .set_should_show_update_notification(false, cx)
                         .detach_and_log_err(cx);
-                });
+                })?;
             }
         }
         anyhow::Ok(())

crates/collab/src/tests/integration_tests.rs 🔗

@@ -6115,7 +6115,8 @@ async fn test_basic_following(
         .update(cx_a, |workspace, cx| {
             workspace::Pane::go_back(workspace, None, cx)
         })
-        .await;
+        .await
+        .unwrap();
     deterministic.run_until_parked();
     workspace_b.read_with(cx_b, |workspace, cx| {
         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());
@@ -6125,7 +6126,8 @@ async fn test_basic_following(
         .update(cx_a, |workspace, cx| {
             workspace::Pane::go_back(workspace, None, cx)
         })
-        .await;
+        .await
+        .unwrap();
     deterministic.run_until_parked();
     workspace_b.read_with(cx_b, |workspace, cx| {
         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b2.id());
@@ -6135,7 +6137,8 @@ async fn test_basic_following(
         .update(cx_a, |workspace, cx| {
             workspace::Pane::go_forward(workspace, None, cx)
         })
-        .await;
+        .await
+        .unwrap();
     deterministic.run_until_parked();
     workspace_b.read_with(cx_b, |workspace, cx| {
         assert_eq!(workspace.active_item(cx).unwrap().id(), editor_b1.id());

crates/collab_ui/src/contact_finder.rs 🔗

@@ -67,7 +67,7 @@ impl PickerDelegate for ContactFinder {
                 this.update(&mut cx, |this, cx| {
                     this.potential_contacts = potential_contacts.into();
                     cx.notify();
-                });
+                })?;
                 anyhow::Ok(())
             }
             .log_err()

crates/command_palette/src/command_palette.rs 🔗

@@ -7,6 +7,7 @@ use gpui::{
 use picker::{Picker, PickerDelegate};
 use settings::Settings;
 use std::cmp;
+use util::ResultExt;
 use workspace::Workspace;
 
 pub fn init(cx: &mut AppContext) {
@@ -188,7 +189,8 @@ impl PickerDelegate for CommandPalette {
                 } else {
                     this.selected_ix = cmp::min(this.selected_ix, this.matches.len() - 1);
                 }
-            });
+            })
+            .log_err();
         })
     }
 

crates/diagnostics/src/diagnostics.rs 🔗

@@ -213,7 +213,7 @@ impl ProjectDiagnosticsEditor {
                     let buffer = project
                         .update(&mut cx, |project, cx| project.open_buffer(path.clone(), cx))
                         .await?;
-                    this.update(&mut cx, |this, cx| this.populate_excerpts(path, buffer, cx))
+                    this.update(&mut cx, |this, cx| this.populate_excerpts(path, buffer, cx))?;
                 }
                 Result::<_, anyhow::Error>::Ok(())
             }

crates/editor/src/editor.rs 🔗

@@ -20,7 +20,7 @@ mod editor_tests;
 pub mod test;
 
 use aho_corasick::AhoCorasick;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
 use blink_manager::BlinkManager;
 use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
@@ -2397,7 +2397,7 @@ impl Editor {
                         } else if this.hide_context_menu(cx).is_none() {
                             this.update_visible_copilot_suggestion(cx);
                         }
-                    });
+                    })?;
                 }
                 Ok::<_, anyhow::Error>(())
             }
@@ -2534,11 +2534,13 @@ impl Editor {
                 prev_task.await;
                 task = this
                     .upgrade(&cx)
-                    .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
+                    .ok_or_else(|| anyhow!("editor dropped"))?
+                    .update(&mut cx, |this, _| this.code_actions_task.take())?;
             }
 
-            if let Some(this) = this.upgrade(&cx) {
-                this.update(&mut cx, |this, cx| {
+            this.upgrade(&cx)
+                .ok_or_else(|| anyhow!("editor dropped"))?
+                .update(&mut cx, |this, cx| {
                     if this.focused {
                         if let Some((buffer, actions)) = this.available_code_actions.clone() {
                             this.show_context_menu(
@@ -2553,8 +2555,8 @@ impl Editor {
                             );
                         }
                     }
-                })
-            }
+                })?;
+
             Ok::<_, anyhow::Error>(())
         })
         .detach_and_log_err(cx);
@@ -2666,7 +2668,7 @@ impl Editor {
                     cx,
                 );
             });
-        });
+        })?;
 
         Ok(())
     }
@@ -2697,6 +2699,7 @@ impl Editor {
                     });
                     cx.notify();
                 })
+                .log_err();
             }
         }));
         None
@@ -2786,7 +2789,8 @@ impl Editor {
                         cx,
                     );
                     cx.notify();
-                });
+                })
+                .log_err();
             }
         }));
         None
@@ -2823,17 +2827,19 @@ impl Editor {
             let mut completions = Vec::new();
             completions.extend(completion.log_err().into_iter().flatten());
             completions.extend(completions_cycling.log_err().into_iter().flatten());
-            this.upgrade(&cx)?.update(&mut cx, |this, cx| {
-                if !completions.is_empty() {
-                    this.copilot_state.completions.clear();
-                    this.copilot_state.active_completion_index = 0;
-                    this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
-                    for completion in completions {
-                        this.copilot_state.push_completion(completion);
+            this.upgrade(&cx)?
+                .update(&mut cx, |this, cx| {
+                    if !completions.is_empty() {
+                        this.copilot_state.completions.clear();
+                        this.copilot_state.active_completion_index = 0;
+                        this.copilot_state.excerpt_id = Some(cursor.excerpt_id);
+                        for completion in completions {
+                            this.copilot_state.push_completion(completion);
+                        }
+                        this.update_visible_copilot_suggestion(cx);
                     }
-                    this.update_visible_copilot_suggestion(cx);
-                }
-            });
+                })
+                .log_err()?;
 
             Some(())
         });
@@ -5416,7 +5422,7 @@ impl Editor {
             let definitions = definitions.await?;
             workspace.update(&mut cx, |workspace, cx| {
                 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
-            });
+            })?;
 
             Ok::<(), anyhow::Error>(())
         })
@@ -5517,7 +5523,7 @@ impl Editor {
                     Self::open_locations_in_multibuffer(
                         workspace, locations, replica_id, title, cx,
                     );
-                });
+                })?;
 
                 Ok(())
             },
@@ -5705,7 +5711,7 @@ impl Editor {
                         editor: rename_editor,
                         block_id,
                     });
-                });
+                })?;
             }
 
             Ok(())
@@ -5751,7 +5757,7 @@ impl Editor {
 
             editor.update(&mut cx, |editor, cx| {
                 editor.refresh_document_highlights(cx);
-            });
+            })?;
             Ok(())
         }))
     }
@@ -6552,9 +6558,16 @@ impl Editor {
         let position = action.position;
         let anchor = action.anchor;
         cx.spawn_weak(|_, mut cx| async move {
-            let editor = editor.await.log_err()?.downcast::<Editor>()?;
+            let editor = editor
+                .await?
+                .downcast::<Editor>()
+                .ok_or_else(|| anyhow!("opened item was not an editor"))?;
             editor.update(&mut cx, |editor, cx| {
-                let buffer = editor.buffer().read(cx).as_singleton()?;
+                let buffer = editor
+                    .buffer()
+                    .read(cx)
+                    .as_singleton()
+                    .ok_or_else(|| anyhow!("cannot jump in a multi-buffer"))?;
                 let buffer = buffer.read(cx);
                 let cursor = if buffer.can_resolve(&anchor) {
                     language::ToPoint::to_point(&anchor, buffer)
@@ -6568,11 +6581,11 @@ impl Editor {
                 });
                 editor.nav_history = nav_history;
 
-                Some(())
-            })?;
-            Some(())
+                anyhow::Ok(())
+            })??;
+            anyhow::Ok(())
         })
-        .detach()
+        .detach_and_log_err(cx);
     }
 
     fn marked_text_ranges(&self, cx: &AppContext) -> Option<Vec<Range<OffsetUtf16>>> {

crates/editor/src/hover_popover.rs 🔗

@@ -208,7 +208,7 @@ fn show_hover(
                             local_diagnostic,
                             primary_diagnostic,
                         });
-                });
+                })?;
             }
 
             // Construct new hover popover from hover request
@@ -254,7 +254,7 @@ fn show_hover(
 
                     this.hover_state.info_popover = hover_popover;
                     cx.notify();
-                });
+                })?;
             }
             Ok::<_, anyhow::Error>(())
         }

crates/editor/src/items.rs 🔗

@@ -80,7 +80,9 @@ impl FollowableItem for Editor {
                 })
             });
 
-            let editor = editor.unwrap_or_else(|| {
+            let editor = if let Some(editor) = editor {
+                editor
+            } else {
                 pane.update(&mut cx, |_, cx| {
                     let multibuffer = cx.add_model(|cx| {
                         let mut multibuffer;
@@ -116,8 +118,8 @@ impl FollowableItem for Editor {
                     });
 
                     cx.add_view(|cx| Editor::for_multibuffer(multibuffer, Some(project), cx))
-                })
-            });
+                })?
+            };
 
             editor.update(&mut cx, |editor, cx| {
                 editor.remote_id = Some(remote_id);
@@ -154,7 +156,7 @@ impl FollowableItem for Editor {
                 }
 
                 anyhow::Ok(())
-            })?;
+            })??;
 
             Ok(editor)
         }))
@@ -387,7 +389,7 @@ impl FollowableItem for Editor {
                         offset: vec2f(message.scroll_x, message.scroll_y)
                     }, cx);
                 }
-            });
+            })?;
             Ok(())
         })
     }
@@ -670,7 +672,7 @@ impl Item for Editor {
             let transaction = reload_buffers.log_err().await;
             this.update(&mut cx, |editor, cx| {
                 editor.request_autoscroll(Autoscroll::fit(), cx)
-            });
+            })?;
             buffer.update(&mut cx, |buffer, _| {
                 if let Some(transaction) = transaction {
                     if !buffer.is_singleton() {

crates/editor/src/scroll.rs 🔗

@@ -248,10 +248,12 @@ impl ScrollManager {
             self.hide_scrollbar_task = Some(cx.spawn_weak(|editor, mut cx| async move {
                 cx.background().timer(SCROLLBAR_SHOW_INTERVAL).await;
                 if let Some(editor) = editor.upgrade(&cx) {
-                    editor.update(&mut cx, |editor, cx| {
-                        editor.scroll_manager.show_scrollbars = false;
-                        cx.notify();
-                    });
+                    editor
+                        .update(&mut cx, |editor, cx| {
+                            editor.scroll_manager.show_scrollbars = false;
+                            cx.notify();
+                        })
+                        .log_err();
                 }
             }));
         } else {

crates/feedback/src/feedback_editor.rs 🔗

@@ -132,9 +132,12 @@ impl FeedbackEditor {
 
             if answer == Some(0) {
                 match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await {
-                    Ok(_) => this.update(&mut cx, |_, cx| {
-                        cx.dispatch_action(workspace::CloseActiveItem);
-                    }),
+                    Ok(_) => {
+                        this.update(&mut cx, |_, cx| {
+                            cx.dispatch_action(workspace::CloseActiveItem);
+                        })
+                        .log_err();
+                    }
                     Err(error) => {
                         log::error!("{}", error);
                         this.update(&mut cx, |_, cx| {
@@ -144,6 +147,7 @@ impl FeedbackEditor {
                                 &["OK"],
                             );
                         })
+                        .log_err();
                     }
                 }
             }
@@ -213,10 +217,10 @@ impl FeedbackEditor {
                             .add_view(|cx| FeedbackEditor::new(system_specs, project, buffer, cx));
                         workspace.add_item(Box::new(feedback_editor), cx);
                     })
-                })
-                .await;
+                })?
+                .await
         })
-        .detach();
+        .detach_and_log_err(cx);
     }
 }
 

crates/file_finder/src/file_finder.rs 🔗

@@ -13,7 +13,7 @@ use std::{
         Arc,
     },
 };
-use util::post_inc;
+use util::{post_inc, ResultExt};
 use workspace::Workspace;
 
 pub struct FileFinder {
@@ -186,7 +186,8 @@ impl FileFinder {
             let did_cancel = cancel_flag.load(atomic::Ordering::Relaxed);
             this.update(&mut cx, |this, cx| {
                 this.set_matches(search_id, did_cancel, query, matches, cx)
-            });
+            })
+            .log_err();
         })
     }
 

crates/journal/src/journal.rs 🔗

@@ -7,7 +7,6 @@ use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
-use util::TryFutureExt as _;
 use workspace::AppState;
 
 actions!(journal, [NewJournalEntry]);
@@ -44,40 +43,37 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut AppContext) {
         Ok::<_, std::io::Error>((journal_dir, entry_path))
     });
 
-    cx.spawn(|mut cx| {
-        async move {
-            let (journal_dir, entry_path) = create_entry.await?;
-            let (workspace, _) = cx
-                .update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx))
-                .await;
-
-            let opened = workspace
-                .update(&mut cx, |workspace, cx| {
-                    workspace.open_paths(vec![entry_path], true, cx)
-                })
-                .await;
-
-            if let Some(Some(Ok(item))) = opened.first() {
-                if let Some(editor) = item.downcast::<Editor>() {
-                    editor.update(&mut cx, |editor, cx| {
-                        let len = editor.buffer().read(cx).len(cx);
-                        editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                            s.select_ranges([len..len])
-                        });
-                        if len > 0 {
-                            editor.insert("\n\n", cx);
-                        }
-                        editor.insert(&entry_heading, cx);
-                        editor.insert("\n\n", cx);
+    cx.spawn(|mut cx| async move {
+        let (journal_dir, entry_path) = create_entry.await?;
+        let (workspace, _) = cx
+            .update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx))
+            .await?;
+
+        let opened = workspace
+            .update(&mut cx, |workspace, cx| {
+                workspace.open_paths(vec![entry_path], true, cx)
+            })?
+            .await;
+
+        if let Some(Some(Ok(item))) = opened.first() {
+            if let Some(editor) = item.downcast::<Editor>() {
+                editor.update(&mut cx, |editor, cx| {
+                    let len = editor.buffer().read(cx).len(cx);
+                    editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+                        s.select_ranges([len..len])
                     });
-                }
+                    if len > 0 {
+                        editor.insert("\n\n", cx);
+                    }
+                    editor.insert(&entry_heading, cx);
+                    editor.insert("\n\n", cx);
+                })?;
             }
-
-            anyhow::Ok(())
         }
-        .log_err()
+
+        anyhow::Ok(())
     })
-    .detach();
+    .detach_and_log_err(cx);
 }
 
 fn journal_dir(settings: &Settings) -> Option<PathBuf> {

crates/language_selector/Cargo.toml 🔗

@@ -17,5 +17,6 @@ picker = { path = "../picker" }
 project = { path = "../project" }
 theme = { path = "../theme" }
 settings = { path = "../settings" }
+util = { path = "../util" }
 workspace = { path = "../workspace" }
 anyhow = "1.0"

crates/language_selector/src/language_selector.rs 🔗

@@ -1,6 +1,7 @@
 mod active_buffer_language;
 
 pub use active_buffer_language::ActiveBufferLanguage;
+use anyhow::anyhow;
 use editor::Editor;
 use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
 use gpui::{
@@ -12,6 +13,7 @@ use picker::{Picker, PickerDelegate};
 use project::Project;
 use settings::Settings;
 use std::sync::Arc;
+use util::ResultExt;
 use workspace::{AppState, Workspace};
 
 actions!(language_selector, [Toggle]);
@@ -140,12 +142,18 @@ impl PickerDelegate for LanguageSelector {
         if let Some(mat) = self.matches.get(self.selected_index) {
             let language_name = &self.candidates[mat.candidate_id].string;
             let language = self.language_registry.language_for_name(language_name);
-            cx.spawn(|this, mut cx| async move {
+            let project = self.project.downgrade();
+            let buffer = self.buffer.downgrade();
+            cx.spawn_weak(|_, mut cx| async move {
                 let language = language.await?;
-                this.update(&mut cx, |this, cx| {
-                    this.project.update(cx, |project, cx| {
-                        project.set_language_for_buffer(&this.buffer, language, cx);
-                    });
+                let project = project
+                    .upgrade(&cx)
+                    .ok_or_else(|| anyhow!("project was dropped"))?;
+                let buffer = buffer
+                    .upgrade(&cx)
+                    .ok_or_else(|| anyhow!("buffer was dropped"))?;
+                project.update(&mut cx, |project, cx| {
+                    project.set_language_for_buffer(&buffer, language, cx);
                 });
                 anyhow::Ok(())
             })
@@ -170,7 +178,7 @@ impl PickerDelegate for LanguageSelector {
     fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> gpui::Task<()> {
         let background = cx.background().clone();
         let candidates = self.candidates.clone();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_weak(|this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -194,13 +202,16 @@ impl PickerDelegate for LanguageSelector {
                 .await
             };
 
-            this.update(&mut cx, |this, cx| {
-                this.matches = matches;
-                this.selected_index = this
-                    .selected_index
-                    .min(this.matches.len().saturating_sub(1));
-                cx.notify();
-            });
+            if let Some(this) = this.upgrade(&cx) {
+                this.update(&mut cx, |this, cx| {
+                    this.matches = matches;
+                    this.selected_index = this
+                        .selected_index
+                        .min(this.matches.len().saturating_sub(1));
+                    cx.notify();
+                })
+                .log_err();
+            }
         })
     }
 

crates/picker/src/picker.rs 🔗

@@ -10,6 +10,7 @@ use gpui::{
 use menu::{Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev};
 use parking_lot::Mutex;
 use std::{cmp, sync::Arc};
+use util::ResultExt;
 
 pub struct Picker<D: PickerDelegate> {
     delegate: WeakViewHandle<D>,
@@ -235,21 +236,23 @@ impl<D: PickerDelegate> Picker<D> {
     pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
         if let Some(delegate) = self.delegate.upgrade(cx) {
             let update = delegate.update(cx, |d, cx| d.update_matches(query, cx));
-            cx.spawn(|this, mut cx| async move {
+            cx.spawn_weak(|this, mut cx| async move {
                 update.await;
-                this.update(&mut cx, |this, cx| {
-                    if let Some(delegate) = this.delegate.upgrade(cx) {
-                        let delegate = delegate.read(cx);
-                        let index = delegate.selected_index();
-                        let target = if delegate.center_selection_after_match_updates() {
-                            ScrollTarget::Center(index)
-                        } else {
-                            ScrollTarget::Show(index)
-                        };
-                        this.list_state.scroll_to(target);
-                        cx.notify();
-                    }
-                });
+                this.upgrade(&cx)?
+                    .update(&mut cx, |this, cx| {
+                        if let Some(delegate) = this.delegate.upgrade(cx) {
+                            let delegate = delegate.read(cx);
+                            let index = delegate.selected_index();
+                            let target = if delegate.center_selection_after_match_updates() {
+                                ScrollTarget::Center(index)
+                            } else {
+                                ScrollTarget::Show(index)
+                            };
+                            this.list_state.scroll_to(target);
+                            cx.notify();
+                        }
+                    })
+                    .log_err()
             })
             .detach()
         }

crates/project_panel/src/project_panel.rs 🔗

@@ -498,7 +498,7 @@ impl ProjectPanel {
             this.update(&mut cx, |this, cx| {
                 this.edit_state.take();
                 cx.notify();
-            });
+            })?;
 
             let new_entry = new_entry?;
             this.update(&mut cx, |this, cx| {
@@ -519,7 +519,7 @@ impl ProjectPanel {
                     );
                 }
                 cx.notify();
-            });
+            })?;
             Ok(())
         }))
     }
@@ -655,7 +655,7 @@ impl ProjectPanel {
                 this.project
                     .update(cx, |project, cx| project.delete_entry(entry_id, cx))
                     .ok_or_else(|| anyhow!("no such entry"))
-            })?
+            })??
             .await
         }))
     }

crates/project_symbols/src/project_symbols.rs 🔗

@@ -155,7 +155,7 @@ impl ProjectSymbolsView {
                                 s.select_ranges([position..position])
                             });
                         });
-                    });
+                    })?;
                     Ok::<_, anyhow::Error>(())
                 })
                 .detach_and_log_err(cx);
@@ -225,7 +225,8 @@ impl PickerDelegate for ProjectSymbolsView {
                         this.external_match_candidates = external_match_candidates;
                         this.symbols = symbols;
                         this.filter(&query, cx);
-                    });
+                    })
+                    .log_err();
                 }
             }
         });

crates/recent_projects/src/recent_projects.rs 🔗

@@ -10,6 +10,7 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation;
 use ordered_float::OrderedFloat;
 use picker::{Picker, PickerDelegate};
 use settings::Settings;
+use util::ResultExt;
 use workspace::{
     notifications::simple_message_notification::MessageNotification, OpenPaths, Workspace,
     WorkspaceLocation, WORKSPACE_DB,
@@ -57,21 +58,23 @@ impl RecentProjectsView {
                 })
                 .await;
 
-            workspace.update(&mut cx, |workspace, cx| {
-                if !workspace_locations.is_empty() {
-                    workspace.toggle_modal(cx, |_, cx| {
-                        let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
-                        cx.subscribe(&view, Self::on_event).detach();
-                        view
-                    });
-                } else {
-                    workspace.show_notification(0, cx, |cx| {
-                        cx.add_view(|_| {
-                            MessageNotification::new_message("No recent projects to open.")
+            workspace
+                .update(&mut cx, |workspace, cx| {
+                    if !workspace_locations.is_empty() {
+                        workspace.toggle_modal(cx, |_, cx| {
+                            let view = cx.add_view(|cx| Self::new(workspace_locations, cx));
+                            cx.subscribe(&view, Self::on_event).detach();
+                            view
+                        });
+                    } else {
+                        workspace.show_notification(0, cx, |cx| {
+                            cx.add_view(|_| {
+                                MessageNotification::new_message("No recent projects to open.")
+                            })
                         })
-                    })
-                }
-            });
+                    }
+                })
+                .log_err();
         })
         .detach();
     }

crates/search/src/buffer_search.rs 🔗

@@ -15,6 +15,7 @@ use project::search::SearchQuery;
 use serde::Deserialize;
 use settings::Settings;
 use std::{any::Any, sync::Arc};
+use util::ResultExt;
 use workspace::{
     item::ItemHandle,
     searchable::{Direction, SearchEvent, SearchableItemHandle, WeakSearchableItemHandle},
@@ -617,7 +618,8 @@ impl BufferSearchBar {
                                 }
                                 cx.notify();
                             }
-                        });
+                        })
+                        .log_err();
                     }
                 }));
             }

crates/terminal_view/src/terminal_view.rs 🔗

@@ -279,7 +279,8 @@ impl TerminalView {
                 async move {
                     Timer::after(CURSOR_BLINK_INTERVAL).await;
                     if let Some(this) = this.upgrade(&cx) {
-                        this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
+                        this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx))
+                            .log_err();
                     }
                 }
             })
@@ -298,6 +299,7 @@ impl TerminalView {
                 Timer::after(CURSOR_BLINK_INTERVAL).await;
                 if let Some(this) = this.upgrade(&cx) {
                     this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
+                        .log_err();
                 }
             }
         })

crates/theme_selector/src/theme_selector.rs 🔗

@@ -8,6 +8,7 @@ use settings::{settings_file::SettingsFile, Settings};
 use staff_mode::StaffMode;
 use std::sync::Arc;
 use theme::{Theme, ThemeMeta, ThemeRegistry};
+use util::ResultExt;
 use workspace::{AppState, Workspace};
 
 pub struct ThemeSelector {
@@ -185,7 +186,7 @@ impl PickerDelegate for ThemeSelector {
             })
             .collect::<Vec<_>>();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_weak(|this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -209,14 +210,17 @@ impl PickerDelegate for ThemeSelector {
                 .await
             };
 
-            this.update(&mut cx, |this, cx| {
-                this.matches = matches;
-                this.selected_index = this
-                    .selected_index
-                    .min(this.matches.len().saturating_sub(1));
-                this.show_selected_theme(cx);
-                cx.notify();
-            });
+            if let Some(this) = this.upgrade(&cx) {
+                this.update(&mut cx, |this, cx| {
+                    this.matches = matches;
+                    this.selected_index = this
+                        .selected_index
+                        .min(this.matches.len().saturating_sub(1));
+                    this.show_selected_theme(cx);
+                    cx.notify();
+                })
+                .log_err();
+            }
         })
     }
 

crates/welcome/src/base_keymap_picker.rs 🔗

@@ -6,6 +6,7 @@ use gpui::{
 };
 use picker::{Picker, PickerDelegate};
 use settings::{settings_file::SettingsFile, BaseKeymap, Settings};
+use util::ResultExt;
 use workspace::Workspace;
 
 pub struct BaseKeymapSelector {
@@ -109,7 +110,7 @@ impl PickerDelegate for BaseKeymapSelector {
             })
             .collect::<Vec<_>>();
 
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn_weak(|this, mut cx| async move {
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -133,13 +134,16 @@ impl PickerDelegate for BaseKeymapSelector {
                 .await
             };
 
-            this.update(&mut cx, |this, cx| {
-                this.matches = matches;
-                this.selected_index = this
-                    .selected_index
-                    .min(this.matches.len().saturating_sub(1));
-                cx.notify();
-            });
+            if let Some(this) = this.upgrade(&cx) {
+                this.update(&mut cx, |this, cx| {
+                    this.matches = matches;
+                    this.selected_index = this
+                        .selected_index
+                        .min(this.matches.len().saturating_sub(1));
+                    cx.notify();
+                })
+                .log_err();
+            }
         })
     }
 

crates/workspace/src/item.rs 🔗

@@ -1,3 +1,17 @@
+use crate::{
+    pane, persistence::model::ItemId, searchable::SearchableItemHandle, DelayedDebouncedEditAction,
+    FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace,
+    WorkspaceId,
+};
+use anyhow::{anyhow, Result};
+use client::{proto, Client};
+use gpui::{
+    fonts::HighlightStyle, AnyViewHandle, AppContext, Element, ModelHandle, Task, View,
+    ViewContext, ViewHandle, WeakViewHandle, WindowContext,
+};
+use project::{Project, ProjectEntryId, ProjectPath};
+use settings::{Autosave, Settings};
+use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
     borrow::Cow,
@@ -12,24 +26,7 @@ use std::{
     },
     time::Duration,
 };
-
-use anyhow::Result;
-use client::{proto, Client};
-use gpui::{
-    fonts::HighlightStyle, AnyViewHandle, AppContext, Element, ModelHandle, Task, View,
-    ViewContext, ViewHandle, WeakViewHandle, WindowContext,
-};
-use project::{Project, ProjectEntryId, ProjectPath};
-use settings::{Autosave, Settings};
-use smallvec::SmallVec;
 use theme::Theme;
-use util::ResultExt;
-
-use crate::{
-    pane, persistence::model::ItemId, searchable::SearchableItemHandle, DelayedDebouncedEditAction,
-    FollowableItemBuilders, ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace,
-    WorkspaceId,
-};
 
 #[derive(Eq, PartialEq, Hash)]
 pub enum ItemEvent {
@@ -461,18 +458,18 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
                                 } else {
                                     cx.spawn_weak(|workspace, mut cx| async move {
                                         workspace
-                                            .upgrade(&cx)?
+                                            .upgrade(&cx)
+                                            .ok_or_else(|| anyhow!("workspace was dropped"))?
                                             .update(&mut cx, |workspace, cx| {
                                                 item.git_diff_recalc(
                                                     workspace.project().clone(),
                                                     cx,
                                                 )
                                             })?
-                                            .await
-                                            .log_err()?;
-                                        Some(())
+                                            .await?;
+                                        anyhow::Ok(())
                                     })
-                                    .detach();
+                                    .detach_and_log_err(cx);
                                 }
                             }
 

crates/workspace/src/pane.rs 🔗

@@ -540,7 +540,7 @@ impl Pane {
                         .update(&mut cx, |workspace, cx| {
                             Self::navigate_history(workspace, pane, mode, cx)
                         })?
-                        .await;
+                        .await?;
                 }
 
                 Ok(())
@@ -1014,10 +1014,10 @@ impl Pane {
                     if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) {
                         pane.remove_item(item_ix, false, cx);
                     }
-                });
+                })?;
             }
 
-            pane.update(&mut cx, |_, cx| cx.notify());
+            pane.update(&mut cx, |_, cx| cx.notify())?;
             Ok(())
         })
     }

crates/workspace/src/persistence/model.rs 🔗

@@ -131,16 +131,21 @@ impl SerializedPaneGroup {
                 ))
             }
             SerializedPaneGroup::Pane(serialized_pane) => {
-                let pane = workspace.update(cx, |workspace, cx| workspace.add_pane(cx))?;
+                let pane = workspace
+                    .update(cx, |workspace, cx| workspace.add_pane(cx))
+                    .log_err()?;
                 let active = serialized_pane.active;
                 serialized_pane
                     .deserialize_to(project, &pane, workspace_id, workspace, cx)
-                    .await;
+                    .await
+                    .log_err()?;
 
                 if pane.read_with(cx, |pane, _| pane.items_len() != 0) {
                     Some((Member::Pane(pane.clone()), active.then(|| pane)))
                 } else {
-                    workspace.update(cx, |workspace, cx| workspace.remove_pane(pane, cx));
+                    workspace
+                        .update(cx, |workspace, cx| workspace.remove_pane(pane, cx))
+                        .log_err()?;
                     None
                 }
             }
@@ -166,7 +171,7 @@ impl SerializedPane {
         workspace_id: WorkspaceId,
         workspace: &ViewHandle<Workspace>,
         cx: &mut AsyncAppContext,
-    ) -> Option<()> {
+    ) -> Result<()> {
         let mut active_item_index = None;
         for (index, item) in self.children.iter().enumerate() {
             let project = project.clone();
@@ -193,7 +198,7 @@ impl SerializedPane {
             if let Some(item_handle) = item_handle {
                 workspace.update(cx, |workspace, cx| {
                     Pane::add_item(workspace, &pane_handle, item_handle, false, false, None, cx);
-                });
+                })?;
             }
 
             if item.active {
@@ -207,7 +212,7 @@ impl SerializedPane {
             })?;
         }
 
-        Some(())
+        anyhow::Ok(())
     }
 }
 

crates/workspace/src/shared_screen.rs 🔗

@@ -2,6 +2,7 @@ use crate::{
     item::{Item, ItemEvent},
     ItemNavHistory, Pane, WorkspaceId,
 };
+use anyhow::Result;
 use call::participant::{Frame, RemoteVideoTrack};
 use client::{proto::PeerId, User};
 use futures::StreamExt;
@@ -25,7 +26,7 @@ pub struct SharedScreen {
     pub peer_id: PeerId,
     user: Arc<User>,
     nav_history: Option<ItemNavHistory>,
-    _maintain_frame: Task<()>,
+    _maintain_frame: Task<Result<()>>,
 }
 
 impl SharedScreen {
@@ -47,9 +48,10 @@ impl SharedScreen {
                     this.update(&mut cx, |this, cx| {
                         this.frame = Some(frame);
                         cx.notify();
-                    })
+                    })?;
                 }
-                this.update(&mut cx, |_, cx| cx.emit(Event::Close));
+                this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
+                Ok(())
             }),
         }
     }

crates/workspace/src/workspace.rs 🔗

@@ -306,7 +306,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
                 let can_close = close.await?;
                 if can_close {
                     cx.update(|cx| open_paths(&action.paths, &app_state, cx))
-                        .await;
+                        .await?;
                 }
                 Ok(())
             }))
@@ -583,10 +583,12 @@ impl DelayedDebouncedEditAction {
             }
 
             if let Some(workspace) = workspace.upgrade(&cx) {
-                workspace
+                if let Some(result) = workspace
                     .update(&mut cx, |workspace, cx| (f)(workspace, cx))
-                    .await
-                    .log_err();
+                    .log_err()
+                {
+                    result.await.log_err();
+                }
             }
         }));
     }
@@ -627,7 +629,7 @@ pub struct Workspace {
     background_actions: BackgroundActions,
     _window_subscriptions: [Subscription; 3],
     _apply_leader_updates: Task<Result<()>>,
-    _observe_current_user: Task<()>,
+    _observe_current_user: Task<Result<()>>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@@ -723,9 +725,10 @@ impl Workspace {
 
             while stream.recv().await.is_some() {
                 if let Some(this) = this.upgrade(&cx) {
-                    this.update(&mut cx, |_, cx| cx.notify());
+                    this.update(&mut cx, |_, cx| cx.notify())?;
                 }
             }
+            anyhow::Ok(())
         });
         let handle = cx.handle();
 
@@ -979,6 +982,7 @@ impl Workspace {
                                         .update(&mut cx, |workspace, cx| {
                                             workspace.open_path(project_path, None, true, cx)
                                         })
+                                        .log_err()?
                                         .await,
                                 )
                             } else {
@@ -1040,13 +1044,13 @@ impl Workspace {
         app_state: &Arc<AppState>,
         cx: &mut ViewContext<Self>,
         callback: F,
-    ) -> Task<T>
+    ) -> Task<Result<T>>
     where
         T: 'static,
         F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
     {
         if self.project.read(cx).is_local() {
-            Task::Ready(Some(callback(self, cx)))
+            Task::Ready(Some(Ok(callback(self, cx))))
         } else {
             let task = Self::new_local(Vec::new(), app_state.clone(), cx);
             cx.spawn(|_vh, mut cx| async move {
@@ -1097,13 +1101,11 @@ impl Workspace {
         _: &CloseWindow,
         cx: &mut ViewContext<Self>,
     ) -> Option<Task<Result<()>>> {
+        let window_id = cx.window_id();
         let prepare = self.prepare_to_close(false, cx);
-        Some(cx.spawn(|this, mut cx| async move {
+        Some(cx.spawn_weak(|_, mut cx| async move {
             if prepare.await? {
-                this.update(&mut cx, |_, cx| {
-                    let window_id = cx.window_id();
-                    cx.remove_window(window_id);
-                });
+                cx.remove_window(window_id);
             }
             Ok(())
         }))
@@ -1155,7 +1157,7 @@ impl Workspace {
             }
 
             Ok(this
-                .update(&mut cx, |this, cx| this.save_all_internal(true, cx))
+                .update(&mut cx, |this, cx| this.save_all_internal(true, cx))?
                 .await?)
         })
     }
@@ -1233,13 +1235,16 @@ impl Workspace {
         cx.spawn(|this, mut cx| async move {
             let mut project_paths = Vec::new();
             for path in &abs_paths {
-                project_paths.push(
-                    this.update(&mut cx, |this, cx| {
+                if let Some(project_path) = this
+                    .update(&mut cx, |this, cx| {
                         Workspace::project_path_for_path(this.project.clone(), path, visible, cx)
                     })
-                    .await
-                    .log_err(),
-                );
+                    .log_err()
+                {
+                    project_paths.push(project_path.await.log_err());
+                } else {
+                    project_paths.push(None);
+                }
             }
 
             let tasks = abs_paths
@@ -1257,6 +1262,7 @@ impl Workspace {
                                     this.update(&mut cx, |this, cx| {
                                         this.open_path(project_path, None, true, cx)
                                     })
+                                    .log_err()?
                                     .await,
                                 )
                             } else {
@@ -1280,14 +1286,15 @@ impl Workspace {
         cx.spawn(|this, mut cx| async move {
             if let Some(paths) = paths.recv().await.flatten() {
                 let results = this
-                    .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))
+                    .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))?
                     .await;
                 for result in results.into_iter().flatten() {
                     result.log_err();
                 }
             }
+            anyhow::Ok(())
         })
-        .detach();
+        .detach_and_log_err(cx);
     }
 
     fn remove_folder_from_project(
@@ -1406,7 +1413,7 @@ impl Workspace {
                     cx.spawn(|this, mut cx| async move {
                         let answer = answer.recv().await;
                         if answer == Some(0) {
-                            this.update(&mut cx, |this, cx| item.save(this.project.clone(), cx))
+                            this.update(&mut cx, |this, cx| item.save(this.project.clone(), cx))?
                                 .await?;
                         }
                         Ok(())
@@ -1423,7 +1430,7 @@ impl Workspace {
                 let mut abs_path = cx.prompt_for_new_path(&start_abs_path);
                 cx.spawn(|this, mut cx| async move {
                     if let Some(abs_path) = abs_path.recv().await.flatten() {
-                        this.update(&mut cx, |_, cx| item.save_as(project, abs_path, cx))
+                        this.update(&mut cx, |_, cx| item.save_as(project, abs_path, cx))?
                             .await?;
                     }
                     Ok(())
@@ -1588,14 +1595,7 @@ impl Workspace {
                 .upgrade(&cx)
                 .ok_or_else(|| anyhow!("pane was closed"))?;
             this.update(&mut cx, |this, cx| {
-                Ok(Pane::open_item(
-                    this,
-                    pane,
-                    project_entry_id,
-                    focus_item,
-                    cx,
-                    build_item,
-                ))
+                Pane::open_item(this, pane, project_entry_id, focus_item, cx, build_item)
             })
         })
     }
@@ -1958,7 +1958,7 @@ impl Workspace {
                         None
                     };
                     Ok::<_, anyhow::Error>(())
-                })?;
+                })??;
                 Self::add_views_from_leader(
                     this.clone(),
                     leader_id,
@@ -1967,7 +1967,7 @@ impl Workspace {
                     &mut cx,
                 )
                 .await?;
-                this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx));
+                this.update(&mut cx, |this, cx| this.leader_updated(leader_id, cx))?;
             }
             Ok(())
         }))
@@ -2245,7 +2245,7 @@ impl Workspace {
                     })
                     .collect(),
             })
-        })
+        })?
     }
 
     async fn handle_unfollow(
@@ -2260,7 +2260,7 @@ impl Workspace {
                 .remove(&envelope.original_sender_id()?);
             cx.notify();
             Ok(())
-        })
+        })?
     }
 
     async fn handle_update_followers(
@@ -2297,7 +2297,7 @@ impl Workspace {
                         }
                     }
                     anyhow::Ok(())
-                })?;
+                })??;
             }
             proto::update_followers::Variant::UpdateView(update_view) => {
                 let variant = update_view
@@ -2318,7 +2318,7 @@ impl Workspace {
                         }
                     }
                     anyhow::Ok(())
-                })?;
+                })??;
                 try_join_all(tasks).await.log_err();
             }
             proto::update_followers::Variant::CreateView(view) => {
@@ -2333,7 +2333,7 @@ impl Workspace {
                 Self::add_views_from_leader(this.clone(), leader_id, panes, vec![view], cx).await?;
             }
         }
-        this.update(cx, |this, cx| this.leader_updated(leader_id, cx));
+        this.update(cx, |this, cx| this.leader_updated(leader_id, cx))?;
         Ok(())
     }
 
@@ -2403,7 +2403,7 @@ impl Workspace {
                 }
 
                 Some(())
-            });
+            })?;
         }
         Ok(())
     }
@@ -2685,7 +2685,7 @@ impl Workspace {
                         &workspace,
                         &mut cx,
                     )
-                    .await;
+                    .await?;
 
                 // Traverse the splits tree and add to things
                 let center_group = serialized_workspace
@@ -2737,13 +2737,14 @@ impl Workspace {
                     });
 
                     cx.notify();
-                });
+                })?;
 
                 // Serialize ourself to make sure our timestamps and any pane / item changes are replicated
                 workspace.read_with(&cx, |workspace, cx| workspace.serialize_workspace(cx))
             }
+            anyhow::Ok(())
         })
-        .detach();
+        .detach_and_log_err(cx);
     }
 
     #[cfg(any(test, feature = "test-support"))]
@@ -2753,8 +2754,8 @@ impl Workspace {
 }
 
 fn notify_if_database_failed(workspace: &ViewHandle<Workspace>, cx: &mut AsyncAppContext) {
-    if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
-        workspace.update(cx, |workspace, cx| {
+    workspace.update(cx, |workspace, cx| {
+        if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) {
             workspace.show_notification_once(0, cx, |cx| {
                 cx.add_view(|_| {
                     MessageNotification::new(
@@ -2766,11 +2767,9 @@ fn notify_if_database_failed(workspace: &ViewHandle<Workspace>, cx: &mut AsyncAp
                     )
                 })
             });
-        });
-    } else {
-        let backup_path = (*db::BACKUP_DB_PATH).read();
-        if let Some(backup_path) = &*backup_path {
-            workspace.update(cx, |workspace, cx| {
+        } else {
+            let backup_path = (*db::BACKUP_DB_PATH).read();
+            if let Some(backup_path) = &*backup_path {
                 workspace.show_notification_once(0, cx, |cx| {
                     cx.add_view(|_| {
                         let backup_path = backup_path.to_string_lossy();
@@ -2788,9 +2787,9 @@ fn notify_if_database_failed(workspace: &ViewHandle<Workspace>, cx: &mut AsyncAp
                         )
                     })
                 });
-            });
+            }
         }
-    }
+    }).log_err();
 }
 
 impl Entity for Workspace {
@@ -3017,10 +3016,12 @@ pub fn open_paths(
     abs_paths: &[PathBuf],
     app_state: &Arc<AppState>,
     cx: &mut AppContext,
-) -> Task<(
-    ViewHandle<Workspace>,
-    Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
-)> {
+) -> Task<
+    Result<(
+        ViewHandle<Workspace>,
+        Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
+    )>,
+> {
     log::info!("open paths {:?}", abs_paths);
 
     // Open paths in existing workspace if possible
@@ -3031,14 +3032,14 @@ pub fn open_paths(
     let abs_paths = abs_paths.to_vec();
     cx.spawn(|mut cx| async move {
         if let Some(existing) = existing {
-            (
+            Ok((
                 existing.clone(),
                 existing
                     .update(&mut cx, |workspace, cx| {
                         workspace.open_paths(abs_paths, true, cx)
-                    })
+                    })?
                     .await,
-            )
+            ))
         } else {
             let contains_directory =
                 futures::future::join_all(abs_paths.iter().map(|path| app_state.fs.is_file(path)))
@@ -3055,9 +3056,9 @@ pub fn open_paths(
                         if contains_directory {
                             workspace.toggle_sidebar(SidebarSide::Left, cx);
                         }
-                    });
+                    })?;
 
-                    (workspace, items)
+                    anyhow::Ok((workspace, items))
                 })
             })
             .await
@@ -3074,11 +3075,13 @@ pub fn open_new(
     cx.spawn(|mut cx| async move {
         let (workspace, opened_paths) = task.await;
 
-        workspace.update(&mut cx, |workspace, cx| {
-            if opened_paths.is_empty() {
-                init(workspace, cx)
-            }
-        })
+        workspace
+            .update(&mut cx, |workspace, cx| {
+                if opened_paths.is_empty() {
+                    init(workspace, cx)
+                }
+            })
+            .log_err();
     })
 }
 
@@ -3702,7 +3705,8 @@ mod tests {
             .update(cx, |workspace, cx| {
                 Pane::go_back(workspace, Some(pane.clone()), cx)
             })
-            .await;
+            .await
+            .unwrap();
 
         assert_eq!(*toolbar_notify_count.borrow(), 3);
         pane.read_with(cx, |pane, _| {

crates/zed/src/main.rs 🔗

@@ -606,71 +606,87 @@ async fn handle_cli_connection(
                 } else {
                     paths
                 };
-                let (workspace, items) = cx
-                    .update(|cx| workspace::open_paths(&paths, &app_state, cx))
-                    .await;
 
                 let mut errored = false;
-                let mut item_release_futures = Vec::new();
-                cx.update(|cx| {
-                    for (item, path) in items.into_iter().zip(&paths) {
-                        match item {
-                            Some(Ok(item)) => {
-                                let released = oneshot::channel();
-                                item.on_release(
-                                    cx,
-                                    Box::new(move |_| {
-                                        let _ = released.0.send(());
-                                    }),
-                                )
-                                .detach();
-                                item_release_futures.push(released.1);
+                match cx
+                    .update(|cx| workspace::open_paths(&paths, &app_state, cx))
+                    .await
+                {
+                    Ok((workspace, items)) => {
+                        let mut item_release_futures = Vec::new();
+                        cx.update(|cx| {
+                            for (item, path) in items.into_iter().zip(&paths) {
+                                match item {
+                                    Some(Ok(item)) => {
+                                        let released = oneshot::channel();
+                                        item.on_release(
+                                            cx,
+                                            Box::new(move |_| {
+                                                let _ = released.0.send(());
+                                            }),
+                                        )
+                                        .detach();
+                                        item_release_futures.push(released.1);
+                                    }
+                                    Some(Err(err)) => {
+                                        responses
+                                            .send(CliResponse::Stderr {
+                                                message: format!(
+                                                    "error opening {:?}: {}",
+                                                    path, err
+                                                ),
+                                            })
+                                            .log_err();
+                                        errored = true;
+                                    }
+                                    None => {}
+                                }
                             }
-                            Some(Err(err)) => {
-                                responses
-                                    .send(CliResponse::Stderr {
-                                        message: format!("error opening {:?}: {}", path, err),
-                                    })
-                                    .log_err();
-                                errored = true;
+                        });
+
+                        if wait {
+                            let background = cx.background();
+                            let wait = async move {
+                                if paths.is_empty() {
+                                    let (done_tx, done_rx) = oneshot::channel();
+                                    let _subscription = cx.update(|cx| {
+                                        cx.observe_release(&workspace, move |_, _| {
+                                            let _ = done_tx.send(());
+                                        })
+                                    });
+                                    drop(workspace);
+                                    let _ = done_rx.await;
+                                } else {
+                                    let _ =
+                                        futures::future::try_join_all(item_release_futures).await;
+                                };
                             }
-                            None => {}
-                        }
-                    }
-                });
-
-                if wait {
-                    let background = cx.background();
-                    let wait = async move {
-                        if paths.is_empty() {
-                            let (done_tx, done_rx) = oneshot::channel();
-                            let _subscription = cx.update(|cx| {
-                                cx.observe_release(&workspace, move |_, _| {
-                                    let _ = done_tx.send(());
-                                })
-                            });
-                            drop(workspace);
-                            let _ = done_rx.await;
-                        } else {
-                            let _ = futures::future::try_join_all(item_release_futures).await;
-                        };
-                    }
-                    .fuse();
-                    futures::pin_mut!(wait);
-
-                    loop {
-                        // Repeatedly check if CLI is still open to avoid wasting resources
-                        // waiting for files or workspaces to close.
-                        let mut timer = background.timer(Duration::from_secs(1)).fuse();
-                        futures::select_biased! {
-                            _ = wait => break,
-                            _ = timer => {
-                                if responses.send(CliResponse::Ping).is_err() {
-                                    break;
+                            .fuse();
+                            futures::pin_mut!(wait);
+
+                            loop {
+                                // Repeatedly check if CLI is still open to avoid wasting resources
+                                // waiting for files or workspaces to close.
+                                let mut timer = background.timer(Duration::from_secs(1)).fuse();
+                                futures::select_biased! {
+                                    _ = wait => break,
+                                    _ = timer => {
+                                        if responses.send(CliResponse::Ping).is_err() {
+                                            break;
+                                        }
+                                    }
                                 }
                             }
                         }
                     }
+                    Err(error) => {
+                        errored = true;
+                        responses
+                            .send(CliResponse::Stderr {
+                                message: format!("error opening {:?}: {}", paths, error),
+                            })
+                            .log_err();
+                    }
                 }
 
                 responses

crates/zed/src/zed.rs 🔗

@@ -247,10 +247,10 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
                                 cx,
                             );
                         })
-                    })
-                    .await;
+                    })?
+                    .await
             })
-            .detach();
+            .detach_and_log_err(cx);
         }
     });
     cx.add_action(
@@ -399,7 +399,7 @@ fn restart(_: &Restart, cx: &mut gpui::AppContext) {
             if !workspace
                 .update(&mut cx, |workspace, cx| {
                     workspace.prepare_to_close(true, cx)
-                })
+                })?
                 .await?
             {
                 return Ok(());
@@ -444,7 +444,7 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) {
             if !workspace
                 .update(&mut cx, |workspace, cx| {
                     workspace.prepare_to_close(true, cx)
-                })
+                })?
                 .await?
             {
                 return Ok(());
@@ -481,8 +481,8 @@ fn open_config_file(
                 workspace.with_local_workspace(&app_state, cx, |workspace, cx| {
                     workspace.open_paths(vec![path.to_path_buf()], false, cx)
                 })
-            })
-            .await
+            })?
+            .await?
             .await;
         Ok::<_, anyhow::Error>(())
     })
@@ -521,25 +521,25 @@ fn open_log_file(
                         .flat_map(|line| [line, "\n"])
                         .collect::<String>();
 
-                    workspace.update(&mut cx, |workspace, cx| {
-                        let project = workspace.project().clone();
-                        let buffer = project
-                            .update(cx, |project, cx| project.create_buffer("", None, cx))
-                            .expect("creating buffers on a local workspace always succeeds");
-                        buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx));
-
-                        let buffer = cx.add_model(|cx| {
-                            MultiBuffer::singleton(buffer, cx).with_title("Log".into())
-                        });
-                        workspace.add_item(
-                            Box::new(
-                                cx.add_view(|cx| {
+                    workspace
+                        .update(&mut cx, |workspace, cx| {
+                            let project = workspace.project().clone();
+                            let buffer = project
+                                .update(cx, |project, cx| project.create_buffer("", None, cx))
+                                .expect("creating buffers on a local workspace always succeeds");
+                            buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx));
+
+                            let buffer = cx.add_model(|cx| {
+                                MultiBuffer::singleton(buffer, cx).with_title("Log".into())
+                            });
+                            workspace.add_item(
+                                Box::new(cx.add_view(|cx| {
                                     Editor::for_multibuffer(buffer, Some(project), cx)
-                                }),
-                            ),
-                            cx,
-                        );
-                    });
+                                })),
+                                cx,
+                            );
+                        })
+                        .log_err();
                 }
             })
             .detach();
@@ -601,7 +601,7 @@ fn open_telemetry_log_file(
                     Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
                     cx,
                 );
-            });
+            }).log_err()?;
 
             Some(())
         })
@@ -642,10 +642,10 @@ fn open_bundled_file(
                         cx,
                     );
                 })
-            })
-            .await;
+            })?
+            .await
     })
-    .detach();
+    .detach_and_log_err(cx);
 }
 
 #[cfg(test)]
@@ -705,11 +705,13 @@ mod tests {
                 cx,
             )
         })
-        .await;
+        .await
+        .unwrap();
         assert_eq!(cx.window_ids().len(), 1);
 
         cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(cx.window_ids().len(), 1);
         let workspace_1 = cx
             .read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
@@ -729,7 +731,8 @@ mod tests {
                 cx,
             )
         })
-        .await;
+        .await
+        .unwrap();
         assert_eq!(cx.window_ids().len(), 2);
 
         // Replace existing windows
@@ -741,7 +744,8 @@ mod tests {
                 cx,
             )
         })
-        .await;
+        .await
+        .unwrap();
         assert_eq!(cx.window_ids().len(), 2);
         cx.read_window(window_id, |cx| {
             let workspace = cx.root_view().clone().downcast::<Workspace>().unwrap();
@@ -768,7 +772,8 @@ mod tests {
             .await;
 
         cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(cx.window_ids().len(), 1);
 
         // When opening the workspace, the window is not in a edited state.
@@ -810,7 +815,8 @@ mod tests {
 
         // Opening the buffer again doesn't impact the window's edited state.
         cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
-            .await;
+            .await
+            .unwrap();
         let editor = workspace.read_with(cx, |workspace, cx| {
             workspace
                 .active_item(cx)
@@ -1475,7 +1481,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file3.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1483,7 +1490,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file2.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1491,7 +1499,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(10, 0), 0.)
@@ -1499,7 +1508,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1508,7 +1518,8 @@ mod tests {
         // Go back one more time and ensure we don't navigate past the first item in the history.
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1516,7 +1527,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_forward(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(10, 0), 0.)
@@ -1524,7 +1536,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_forward(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file2.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1542,7 +1555,8 @@ mod tests {
             .unwrap();
         workspace
             .update(cx, |w, cx| Pane::go_forward(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file3.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1550,7 +1564,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_forward(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file3.clone(), DisplayPoint::new(16, 0), 12.5)
@@ -1558,7 +1573,8 @@ mod tests {
 
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file3.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1580,14 +1596,16 @@ mod tests {
             .unwrap();
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(10, 0), 0.)
         );
         workspace
             .update(cx, |w, cx| Pane::go_forward(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file3.clone(), DisplayPoint::new(0, 0), 0.)
@@ -1630,14 +1648,16 @@ mod tests {
         });
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(2, 0), 0.)
         );
         workspace
             .update(cx, |w, cx| Pane::go_back(w, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(
             active_location(&workspace, cx),
             (file1.clone(), DisplayPoint::new(3, 0), 0.)
@@ -1751,61 +1771,84 @@ mod tests {
 
         // Reopen all the closed items, ensuring they are reopened in the same order
         // in which they were closed.
-        workspace.update(cx, Pane::reopen_closed_item).await;
+        workspace
+            .update(cx, Pane::reopen_closed_item)
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
 
-        workspace.update(cx, Pane::reopen_closed_item).await;
+        workspace
+            .update(cx, Pane::reopen_closed_item)
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file2.clone()));
 
-        workspace.update(cx, Pane::reopen_closed_item).await;
+        workspace
+            .update(cx, Pane::reopen_closed_item)
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file4.clone()));
 
-        workspace.update(cx, Pane::reopen_closed_item).await;
+        workspace
+            .update(cx, Pane::reopen_closed_item)
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file1.clone()));
 
         // Reopening past the last closed item is a no-op.
-        workspace.update(cx, Pane::reopen_closed_item).await;
+        workspace
+            .update(cx, Pane::reopen_closed_item)
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file1.clone()));
 
         // Reopening closed items doesn't interfere with navigation history.
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file4.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file2.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file4.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file2.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file1.clone()));
 
         workspace
             .update(cx, |workspace, cx| Pane::go_back(workspace, None, cx))
-            .await;
+            .await
+            .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file1.clone()));
 
         fn active_path(