Switch branches within spawn()

Piotr Osiewicz created

Change summary

crates/collab_ui/src/branch_list.rs | 87 ++++++++++++++++--------------
1 file changed, 46 insertions(+), 41 deletions(-)

Detailed changes

crates/collab_ui/src/branch_list.rs 🔗

@@ -1,3 +1,4 @@
+use anyhow::{anyhow, bail};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle};
 use picker::{Picker, PickerDelegate, PickerEvent};
@@ -53,7 +54,7 @@ impl PickerDelegate for BranchListDelegate {
 
     fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
         cx.spawn(move |picker, mut cx| async move {
-            let candidates = picker
+            let Some(candidates) = picker
                 .read_with(&mut cx, |view, cx| {
                     let delegate = view.delegate();
                     let project = delegate.workspace.read(cx).project().read(&cx);
@@ -67,13 +68,10 @@ impl PickerDelegate for BranchListDelegate {
                         .path
                         .to_path_buf();
                     cwd.push(".git");
-                    let mut branches = project
-                        .fs()
-                        .open_repo(&cwd)
-                        .unwrap()
+                    let Some(repo) = project.fs().open_repo(&cwd) else {bail!("Project does not have associated git repository.")};
+                    let mut branches = repo
                         .lock()
-                        .branches()
-                        .unwrap();
+                        .branches()?;
                     if query.is_empty() {
                         const RECENT_BRANCHES_COUNT: usize = 10;
                         // Do a partial sort to show recent-ish branches first.
@@ -83,7 +81,7 @@ impl PickerDelegate for BranchListDelegate {
                         branches.truncate(RECENT_BRANCHES_COUNT);
                         branches.sort_unstable_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
                     }
-                    branches
+                    Ok(branches
                         .iter()
                         .cloned()
                         .enumerate()
@@ -92,9 +90,10 @@ impl PickerDelegate for BranchListDelegate {
                             char_bag: command.name.chars().collect(),
                             string: command.name.into(),
                         })
-                        .collect::<Vec<_>>()
+                        .collect::<Vec<_>>())
                 })
-                .unwrap();
+                .log_err() else { return; };
+            let Some(candidates) = candidates.log_err() else {return;};
             let matches = if query.is_empty() {
                 candidates
                     .into_iter()
@@ -136,37 +135,43 @@ impl PickerDelegate for BranchListDelegate {
     fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
         let current_pick = self.selected_index();
         let current_pick = self.matches[current_pick].string.clone();
-        let project = self.workspace.read(cx).project().read(cx);
-        let mut cwd = project
-            .visible_worktrees(cx)
-            .next()
-            .unwrap()
-            .read(cx)
-            .root_entry()
-            .unwrap()
-            .path
-            .to_path_buf();
-        cwd.push(".git");
-        let status = project
-            .fs()
-            .open_repo(&cwd)
-            .unwrap()
-            .lock()
-            .change_branch(&current_pick);
-        if status.is_err() {
-            const GIT_CHECKOUT_FAILURE_ID: usize = 2048;
-            self.workspace.update(cx, |model, ctx| {
-                model.show_toast(
-                    Toast::new(
-                        GIT_CHECKOUT_FAILURE_ID,
-                        format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"),
-                    ),
-                    ctx,
-                )
-            });
-        }
-        status.log_err();
-        cx.emit(PickerEvent::Dismiss);
+        cx.spawn(|picker, mut cx| async move {
+            picker.update(&mut cx, |this, cx| {
+                let project = this.delegate().workspace.read(cx).project().read(cx);
+                let mut cwd = project
+                .visible_worktrees(cx)
+                .next()
+                .ok_or_else(|| anyhow!("There are no visisible worktrees."))?
+                .read(cx)
+                .root_entry()
+                .ok_or_else(|| anyhow!("Worktree has no root entry."))?
+                .path
+                .to_path_buf();
+                cwd.push(".git");
+                let status = project
+                    .fs()
+                    .open_repo(&cwd)
+                    .ok_or_else(|| anyhow!("Could not open repository at path `{}`", cwd.as_os_str().to_string_lossy()))?
+                    .lock()
+                    .change_branch(&current_pick);
+                if status.is_err() {
+                    const GIT_CHECKOUT_FAILURE_ID: usize = 2048;
+                    this.delegate().workspace.update(cx, |model, ctx| {
+                        model.show_toast(
+                            Toast::new(
+                                GIT_CHECKOUT_FAILURE_ID,
+                                format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"),
+                            ),
+                            ctx,
+                        )
+                    });
+                    status?;
+                }
+                cx.emit(PickerEvent::Dismiss);
+
+                Ok::<(), anyhow::Error>(())
+            }).log_err();
+        }).detach();
     }
 
     fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {