add user prompt for permission to index the project, for context retrieval

KCaverly created

Change summary

crates/assistant/src/assistant_panel.rs | 77 ++++++++++++++++++++++++--
1 file changed, 71 insertions(+), 6 deletions(-)

Detailed changes

crates/assistant/src/assistant_panel.rs 🔗

@@ -29,7 +29,7 @@ use gpui::{
     },
     fonts::HighlightStyle,
     geometry::vector::{vec2f, Vector2F},
-    platform::{CursorStyle, MouseButton},
+    platform::{CursorStyle, MouseButton, PromptLevel},
     Action, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, ModelContext,
     ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
     WeakModelHandle, WeakViewHandle, WindowContext,
@@ -348,6 +348,8 @@ impl AssistantPanel {
                 self.workspace.clone(),
                 cx,
                 self.retrieve_context_in_next_inline_assist,
+                self.semantic_index.clone(),
+                project.clone(),
             );
             cx.focus_self();
             assistant
@@ -2751,6 +2753,9 @@ struct InlineAssistant {
     codegen: ModelHandle<Codegen>,
     _subscriptions: Vec<Subscription>,
     retrieve_context: bool,
+    semantic_index: Option<ModelHandle<SemanticIndex>>,
+    semantic_permissioned: Option<bool>,
+    project: ModelHandle<Project>,
 }
 
 impl Entity for InlineAssistant {
@@ -2876,6 +2881,8 @@ impl InlineAssistant {
         workspace: WeakViewHandle<Workspace>,
         cx: &mut ViewContext<Self>,
         retrieve_context: bool,
+        semantic_index: Option<ModelHandle<SemanticIndex>>,
+        project: ModelHandle<Project>,
     ) -> Self {
         let prompt_editor = cx.add_view(|cx| {
             let mut editor = Editor::single_line(
@@ -2908,7 +2915,24 @@ impl InlineAssistant {
             codegen,
             _subscriptions: subscriptions,
             retrieve_context,
+            semantic_permissioned: None,
+            semantic_index,
+            project,
+        }
+    }
+
+    fn semantic_permissioned(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<bool>> {
+        if let Some(value) = self.semantic_permissioned {
+            return Task::ready(Ok(value));
         }
+
+        let project = self.project.clone();
+        self.semantic_index
+            .as_mut()
+            .map(|semantic| {
+                semantic.update(cx, |this, cx| this.project_previously_indexed(&project, cx))
+            })
+            .unwrap_or(Task::ready(Ok(false)))
     }
 
     fn handle_prompt_editor_events(
@@ -2980,11 +3004,52 @@ impl InlineAssistant {
     }
 
     fn toggle_retrieve_context(&mut self, _: &ToggleRetrieveContext, cx: &mut ViewContext<Self>) {
-        self.retrieve_context = !self.retrieve_context;
-        cx.emit(InlineAssistantEvent::RetrieveContextToggled {
-            retrieve_context: self.retrieve_context,
-        });
-        cx.notify();
+        let semantic_permissioned = self.semantic_permissioned(cx);
+        let project = self.project.clone();
+        let project_name = project
+            .read(cx)
+            .worktree_root_names(cx)
+            .collect::<Vec<&str>>()
+            .join("/");
+        let is_plural = project_name.chars().filter(|letter| *letter == '/').count() > 0;
+        let prompt_text = format!("Would you like to index the '{}' project{} for context retrieval? This requires sending code to the OpenAI API", project_name,
+            if is_plural {
+                "s"
+            } else {""});
+
+        cx.spawn(|this, mut cx| async move {
+            // If Necessary prompt user
+            if !semantic_permissioned.await.unwrap_or(false) {
+                let mut answer = this.update(&mut cx, |_, cx| {
+                    cx.prompt(
+                        PromptLevel::Info,
+                        prompt_text.as_str(),
+                        &["Continue", "Cancel"],
+                    )
+                })?;
+
+                if answer.next().await == Some(0) {
+                    this.update(&mut cx, |this, _| {
+                        this.semantic_permissioned = Some(true);
+                    })?;
+                } else {
+                    return anyhow::Ok(());
+                }
+            }
+
+            // If permissioned, update context appropriately
+            this.update(&mut cx, |this, cx| {
+                this.retrieve_context = !this.retrieve_context;
+
+                cx.emit(InlineAssistantEvent::RetrieveContextToggled {
+                    retrieve_context: this.retrieve_context,
+                });
+                cx.notify();
+            })?;
+
+            anyhow::Ok(())
+        })
+        .detach_and_log_err(cx);
     }
 
     fn toggle_include_conversation(