Handle loading outside of a project

Conrad Irwin created

Change summary

crates/acp/src/thread_view.rs | 96 ++++++++++++++++++++----------------
1 file changed, 53 insertions(+), 43 deletions(-)

Detailed changes

crates/acp/src/thread_view.rs 🔗

@@ -47,13 +47,64 @@ enum ThreadState {
 
 impl AcpThreadView {
     pub fn new(project: Entity<Project>, window: &mut Window, cx: &mut Context<Self>) -> Self {
+        let message_editor = cx.new(|cx| {
+            let buffer = cx.new(|cx| Buffer::local("", cx));
+            let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
+
+            let mut editor = Editor::new(
+                editor::EditorMode::AutoHeight {
+                    min_lines: 4,
+                    max_lines: None,
+                },
+                buffer,
+                None,
+                window,
+                cx,
+            );
+            editor.set_placeholder_text("Send a message", cx);
+            editor.set_soft_wrap();
+            editor
+        });
+
+        let list_state = ListState::new(
+            0,
+            gpui::ListAlignment::Bottom,
+            px(2048.0),
+            cx.processor({
+                move |this: &mut Self, item: usize, window, cx| {
+                    let Some(entry) = this
+                        .thread()
+                        .and_then(|thread| thread.read(cx).entries.get(item))
+                    else {
+                        return Empty.into_any();
+                    };
+                    this.render_entry(entry, window, cx)
+                }
+            }),
+        );
+
+        Self {
+            thread_state: Self::initial_state(project, window, cx),
+            message_editor,
+            send_task: None,
+            list_state: list_state,
+        }
+    }
+
+    fn initial_state(
+        project: Entity<Project>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> ThreadState {
         let Some(root_dir) = project
             .read(cx)
             .visible_worktrees(cx)
             .next()
             .map(|worktree| worktree.read(cx).abs_path())
         else {
-            todo!();
+            return ThreadState::LoadError(
+                "Gemini threads must be created within a project".into(),
+            );
         };
 
         let cli_path =
@@ -70,25 +121,6 @@ impl AcpThreadView {
             .spawn()
             .unwrap();
 
-        let message_editor = cx.new(|cx| {
-            let buffer = cx.new(|cx| Buffer::local("", cx));
-            let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
-
-            let mut editor = Editor::new(
-                editor::EditorMode::AutoHeight {
-                    min_lines: 4,
-                    max_lines: None,
-                },
-                buffer,
-                None,
-                window,
-                cx,
-            );
-            editor.set_placeholder_text("Send a message", cx);
-            editor.set_soft_wrap();
-            editor
-        });
-
         let project = project.clone();
         let load_task = cx.spawn_in(window, async move |this, cx| {
             let agent = AcpServer::stdio(child, project, cx);
@@ -136,29 +168,7 @@ impl AcpThreadView {
             .log_err();
         });
 
-        let list_state = ListState::new(
-            0,
-            gpui::ListAlignment::Bottom,
-            px(2048.0),
-            cx.processor({
-                move |this: &mut Self, item: usize, window, cx| {
-                    let Some(entry) = this
-                        .thread()
-                        .and_then(|thread| thread.read(cx).entries.get(item))
-                    else {
-                        return Empty.into_any();
-                    };
-                    this.render_entry(entry, window, cx)
-                }
-            }),
-        );
-
-        Self {
-            thread_state: ThreadState::Loading { _task: load_task },
-            message_editor,
-            send_task: None,
-            list_state: list_state,
-        }
+        ThreadState::Loading { _task: load_task }
     }
 
     fn thread(&self) -> Option<&Entity<AcpThread>> {