add worktree previously indexed functionality to vector db

KCaverly created

Change summary

crates/semantic_index/src/db.rs             | 17 +++++++
crates/semantic_index/src/semantic_index.rs | 53 ++++++++++++++++++++++
2 files changed, 69 insertions(+), 1 deletion(-)

Detailed changes

crates/semantic_index/src/db.rs 🔗

@@ -197,6 +197,23 @@ impl VectorDatabase {
         Ok(())
     }
 
+    pub fn worktree_previously_indexed(&self, worktree_root_path: &Path) -> Result<bool> {
+        let mut worktree_query = self
+            .db
+            .prepare("SELECT id FROM worktrees WHERE absolute_path = ?1")?;
+        let worktree_id = worktree_query
+            .query_row(params![worktree_root_path.to_string_lossy()], |row| {
+                Ok(row.get::<_, i64>(0)?)
+            })
+            .map_err(|err| anyhow!(err));
+
+        if worktree_id.is_ok() {
+            return Ok(true);
+        } else {
+            return Ok(false);
+        }
+    }
+
     pub fn find_or_create_worktree(&self, worktree_root_path: &Path) -> Result<i64> {
         // Check that the absolute path doesnt exist
         let mut worktree_query = self

crates/semantic_index/src/semantic_index.rs 🔗

@@ -34,7 +34,7 @@ use util::{
     ResultExt,
 };
 
-const SEMANTIC_INDEX_VERSION: usize = 5;
+const SEMANTIC_INDEX_VERSION: usize = 6;
 const EMBEDDINGS_BATCH_SIZE: usize = 80;
 
 pub fn init(
@@ -161,6 +161,10 @@ enum DbOperation {
         worktree_id: i64,
         sender: oneshot::Sender<Result<HashMap<PathBuf, SystemTime>>>,
     },
+    WorktreePreviouslyIndexed {
+        path: Arc<Path>,
+        sender: oneshot::Sender<Result<bool>>,
+    },
 }
 
 enum EmbeddingJob {
@@ -327,6 +331,10 @@ impl SemanticIndex {
                 let file_mtimes = db.get_file_mtimes(worktree_db_id);
                 sender.send(file_mtimes).ok();
             }
+            DbOperation::WorktreePreviouslyIndexed { path, sender } => {
+                let worktree_indexed = db.worktree_previously_indexed(path.as_ref());
+                sender.send(worktree_indexed).ok();
+            }
         }
     }
 
@@ -479,6 +487,49 @@ impl SemanticIndex {
         async move { rx.await? }
     }
 
+    fn worktree_previously_indexed(&self, path: Arc<Path>) -> impl Future<Output = Result<bool>> {
+        let (tx, rx) = oneshot::channel();
+        self.db_update_tx
+            .try_send(DbOperation::WorktreePreviouslyIndexed { path, sender: tx })
+            .unwrap();
+        async move { rx.await? }
+    }
+
+    pub fn project_previously_indexed(
+        &mut self,
+        project: ModelHandle<Project>,
+        cx: &mut ModelContext<Self>,
+    ) -> Task<Result<bool>> {
+        let worktree_scans_complete = project
+            .read(cx)
+            .worktrees(cx)
+            .map(|worktree| {
+                let scan_complete = worktree.read(cx).as_local().unwrap().scan_complete();
+                async move {
+                    scan_complete.await;
+                }
+            })
+            .collect::<Vec<_>>();
+
+        let worktrees_indexed_previously = project
+            .read(cx)
+            .worktrees(cx)
+            .map(|worktree| self.worktree_previously_indexed(worktree.read(cx).abs_path()))
+            .collect::<Vec<_>>();
+
+        cx.spawn(|this, mut cx| async move {
+            futures::future::join_all(worktree_scans_complete).await;
+
+            let worktree_indexed_previously =
+                futures::future::join_all(worktrees_indexed_previously).await;
+
+            Ok(worktree_indexed_previously
+                .iter()
+                .filter(|worktree| worktree.is_ok())
+                .all(|v| v.as_ref().log_err().is_some_and(|v| v.to_owned())))
+        })
+    }
+
     pub fn index_project(
         &mut self,
         project: ModelHandle<Project>,