Fixed bugs in workflow step preview (#16445)

Mikayla Maki created

Release Notes:

- N/A

Change summary

crates/assistant/src/assistant_panel.rs | 79 ++++++++++++++++++--------
crates/workspace/src/item.rs            | 11 +++
crates/workspace/src/pane.rs            | 17 +++++
crates/workspace/src/workspace.rs       | 44 +++++++++-----
4 files changed, 109 insertions(+), 42 deletions(-)

Detailed changes

crates/assistant/src/assistant_panel.rs 🔗

@@ -2692,14 +2692,25 @@ impl ContextEditor {
     fn update_active_workflow_step(&mut self, cx: &mut ViewContext<Self>) {
         let new_step = self.active_workflow_step_for_cursor(cx);
         if new_step.as_ref() != self.active_workflow_step.as_ref() {
+            let mut old_editor = None;
+            let mut old_editor_was_open = None;
             if let Some(old_step) = self.active_workflow_step.take() {
-                self.hide_workflow_step(old_step.range, cx);
+                (old_editor, old_editor_was_open) =
+                    self.hide_workflow_step(old_step.range, cx).unzip();
             }
 
+            let mut new_editor = None;
             if let Some(new_step) = new_step {
-                self.show_workflow_step(new_step.range.clone(), cx);
+                new_editor = self.show_workflow_step(new_step.range.clone(), cx);
                 self.active_workflow_step = Some(new_step);
             }
+
+            if new_editor != old_editor {
+                if let Some((old_editor, old_editor_was_open)) = old_editor.zip(old_editor_was_open)
+                {
+                    self.close_workflow_editor(cx, old_editor, old_editor_was_open)
+                }
+            }
         }
     }
 
@@ -2707,15 +2718,15 @@ impl ContextEditor {
         &mut self,
         step_range: Range<language::Anchor>,
         cx: &mut ViewContext<Self>,
-    ) {
+    ) -> Option<(View<Editor>, bool)> {
         let Some(step) = self.workflow_steps.get_mut(&step_range) else {
-            return;
+            return None;
         };
         let Some(assist) = step.assist.as_ref() else {
-            return;
+            return None;
         };
         let Some(editor) = assist.editor.upgrade() else {
-            return;
+            return None;
         };
 
         if matches!(step.status(cx), WorkflowStepStatus::Idle) {
@@ -2725,32 +2736,42 @@ impl ContextEditor {
                     assistant.finish_assist(assist_id, true, cx)
                 }
             });
-
-            self.workspace
-                .update(cx, |workspace, cx| {
-                    if let Some(pane) = workspace.pane_for(&editor) {
-                        pane.update(cx, |pane, cx| {
-                            let item_id = editor.entity_id();
-                            if !assist.editor_was_open && pane.is_active_preview_item(item_id) {
-                                pane.close_item_by_id(item_id, SaveIntent::Skip, cx)
-                                    .detach_and_log_err(cx);
-                            }
-                        });
-                    }
-                })
-                .ok();
+            return Some((editor, assist.editor_was_open));
         }
+
+        return None;
+    }
+
+    fn close_workflow_editor(
+        &mut self,
+        cx: &mut ViewContext<ContextEditor>,
+        editor: View<Editor>,
+        editor_was_open: bool,
+    ) {
+        self.workspace
+            .update(cx, |workspace, cx| {
+                if let Some(pane) = workspace.pane_for(&editor) {
+                    pane.update(cx, |pane, cx| {
+                        let item_id = editor.entity_id();
+                        if !editor_was_open && pane.is_active_preview_item(item_id) {
+                            pane.close_item_by_id(item_id, SaveIntent::Skip, cx)
+                                .detach_and_log_err(cx);
+                        }
+                    });
+                }
+            })
+            .ok();
     }
 
     fn show_workflow_step(
         &mut self,
         step_range: Range<language::Anchor>,
         cx: &mut ViewContext<Self>,
-    ) {
+    ) -> Option<View<Editor>> {
         let Some(step) = self.workflow_steps.get_mut(&step_range) else {
-            return;
+            return None;
         };
-
+        let mut editor_to_return = None;
         let mut scroll_to_assist_id = None;
         match step.status(cx) {
             WorkflowStepStatus::Idle => {
@@ -2764,6 +2785,10 @@ impl ContextEditor {
                         &self.workspace,
                         cx,
                     );
+                    editor_to_return = step
+                        .assist
+                        .as_ref()
+                        .and_then(|assist| assist.editor.upgrade());
                 }
             }
             WorkflowStepStatus::Pending => {
@@ -2785,14 +2810,15 @@ impl ContextEditor {
         }
 
         if let Some(assist_id) = scroll_to_assist_id {
-            if let Some(editor) = step
+            if let Some(assist_editor) = step
                 .assist
                 .as_ref()
                 .and_then(|assists| assists.editor.upgrade())
             {
+                editor_to_return = Some(assist_editor.clone());
                 self.workspace
                     .update(cx, |workspace, cx| {
-                        workspace.activate_item(&editor, false, false, cx);
+                        workspace.activate_item(&assist_editor, false, false, cx);
                     })
                     .ok();
                 InlineAssistant::update_global(cx, |assistant, cx| {
@@ -2800,6 +2826,8 @@ impl ContextEditor {
                 });
             }
         }
+
+        return editor_to_return;
     }
 
     fn open_assists_for_step(
@@ -2843,7 +2871,6 @@ impl ContextEditor {
                     )
                 })
                 .log_err()?;
-
             let (&excerpt_id, _, _) = editor
                 .read(cx)
                 .buffer()

crates/workspace/src/item.rs 🔗

@@ -373,6 +373,7 @@ pub trait ItemHandle: 'static + Send {
     fn dragged_tab_content(&self, params: TabContentParams, cx: &WindowContext) -> AnyElement;
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
     fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
+    fn project_paths(&self, cx: &AppContext) -> SmallVec<[ProjectPath; 3]>;
     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[EntityId; 3]>;
     fn for_each_project_item(
         &self,
@@ -531,6 +532,16 @@ impl<T: Item> ItemHandle for View<T> {
         result
     }
 
+    fn project_paths(&self, cx: &AppContext) -> SmallVec<[ProjectPath; 3]> {
+        let mut result = SmallVec::new();
+        self.read(cx).for_each_project_item(cx, &mut |_, item| {
+            if let Some(id) = item.project_path(cx) {
+                result.push(id);
+            }
+        });
+        result
+    }
+
     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[EntityId; 3]> {
         let mut result = SmallVec::new();
         self.read(cx).for_each_project_item(cx, &mut |id, _| {

crates/workspace/src/pane.rs 🔗

@@ -920,7 +920,22 @@ impl Pane {
         cx: &AppContext,
     ) -> Option<Box<dyn ItemHandle>> {
         self.items.iter().find_map(|item| {
-            if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] {
+            if item.is_singleton(cx) && (item.project_entry_ids(cx).as_slice() == [entry_id]) {
+                Some(item.boxed_clone())
+            } else {
+                None
+            }
+        })
+    }
+
+    pub fn item_for_path(
+        &self,
+        project_path: ProjectPath,
+        cx: &AppContext,
+    ) -> Option<Box<dyn ItemHandle>> {
+        self.items.iter().find_map(move |item| {
+            if item.is_singleton(cx) && (item.project_path(cx).as_slice() == [project_path.clone()])
+            {
                 Some(item.boxed_clone())
             } else {
                 None

crates/workspace/src/workspace.rs 🔗

@@ -2618,22 +2618,43 @@ impl Workspace {
         open_project_item
     }
 
-    pub fn is_project_item_open<T>(
+    pub fn find_project_item<T>(
         &self,
         pane: &View<Pane>,
         project_item: &Model<T::Item>,
         cx: &AppContext,
-    ) -> bool
+    ) -> Option<View<T>>
     where
         T: ProjectItem,
     {
         use project::Item as _;
+        let project_item = project_item.read(cx);
+        let entry_id = project_item.entry_id(cx);
+        let project_path = project_item.project_path(cx);
 
-        project_item
-            .read(cx)
-            .entry_id(cx)
-            .and_then(|entry_id| pane.read(cx).item_for_entry(entry_id, cx))
-            .and_then(|item| item.downcast::<T>())
+        let mut item = None;
+        if let Some(entry_id) = entry_id {
+            item = pane.read(cx).item_for_entry(entry_id, cx);
+        }
+        if item.is_none() {
+            if let Some(project_path) = project_path {
+                item = pane.read(cx).item_for_path(project_path, cx);
+            }
+        }
+
+        item.and_then(|item| item.downcast::<T>())
+    }
+
+    pub fn is_project_item_open<T>(
+        &self,
+        pane: &View<Pane>,
+        project_item: &Model<T::Item>,
+        cx: &AppContext,
+    ) -> bool
+    where
+        T: ProjectItem,
+    {
+        self.find_project_item::<T>(pane, project_item, cx)
             .is_some()
     }
 
@@ -2648,19 +2669,12 @@ impl Workspace {
     where
         T: ProjectItem,
     {
-        use project::Item as _;
-
-        let entry_id = project_item.read(cx).entry_id(cx);
-        if let Some(item) = entry_id
-            .and_then(|entry_id| pane.read(cx).item_for_entry(entry_id, cx))
-            .and_then(|item| item.downcast())
-        {
+        if let Some(item) = self.find_project_item(&pane, &project_item, cx) {
             self.activate_item(&item, activate_pane, focus_item, cx);
             return item;
         }
 
         let item = cx.new_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
-
         let item_id = item.item_id();
         let mut destination_index = None;
         pane.update(cx, |pane, cx| {