Use the main thread less on search tool (#26732)

Richard Feldman created

Release Notes:

- N/A

Change summary

Cargo.lock                                     |  1 
crates/assistant_tools/Cargo.toml              |  1 
crates/assistant_tools/src/path_search_tool.rs | 53 ++++++++++---------
3 files changed, 31 insertions(+), 24 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -727,6 +727,7 @@ dependencies = [
  "ui",
  "util",
  "workspace",
+ "worktree",
 ]
 
 [[package]]

crates/assistant_tools/Cargo.toml 🔗

@@ -30,6 +30,7 @@ theme.workspace = true
 ui.workspace = true
 util.workspace = true
 workspace.workspace = true
+worktree.workspace = true
 settings.workspace = true
 
 [dev-dependencies]

crates/assistant_tools/src/path_search_tool.rs 🔗

@@ -1,12 +1,13 @@
 use anyhow::{anyhow, Result};
 use assistant_tool::{ActionLog, Tool};
-use gpui::{App, Entity, Task};
+use gpui::{App, AppContext, Entity, Task};
 use language_model::LanguageModelRequestMessage;
 use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::{path::PathBuf, sync::Arc};
 use util::paths::PathMatcher;
+use worktree::Snapshot;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct PathSearchToolInput {
@@ -56,34 +57,38 @@ impl Tool for PathSearchTool {
             Ok(matcher) => matcher,
             Err(err) => return Task::ready(Err(anyhow!("Invalid glob: {}", err))),
         };
+        let snapshots: Vec<Snapshot> = project
+            .read(cx)
+            .worktrees(cx)
+            .map(|worktree| worktree.read(cx).snapshot())
+            .collect();
 
-        let mut matches = Vec::new();
+        cx.background_spawn(async move {
+            let mut matches = Vec::new();
 
-        for worktree_handle in project.read(cx).worktrees(cx) {
-            let worktree = worktree_handle.read(cx);
-            let root_name = worktree.root_name();
+            for worktree in snapshots {
+                let root_name = worktree.root_name();
 
-            // Don't consider ignored entries.
-            for entry in worktree.entries(false, 0) {
-                if path_matcher.is_match(&entry.path) {
-                    matches.push(
-                        PathBuf::from(root_name)
-                            .join(&entry.path)
-                            .to_string_lossy()
-                            .to_string(),
-                    );
+                // Don't consider ignored entries.
+                for entry in worktree.entries(false, 0) {
+                    if path_matcher.is_match(&entry.path) {
+                        matches.push(
+                            PathBuf::from(root_name)
+                                .join(&entry.path)
+                                .to_string_lossy()
+                                .to_string(),
+                        );
+                    }
                 }
             }
-        }
 
-        if matches.is_empty() {
-            Task::ready(Ok(format!(
-                "No paths in the project matched the glob {glob:?}"
-            )))
-        } else {
-            // Sort to group entries in the same directory together.
-            matches.sort();
-            Task::ready(Ok(matches.join("\n")))
-        }
+            if matches.is_empty() {
+                Ok(format!("No paths in the project matched the glob {glob:?}"))
+            } else {
+                // Sort to group entries in the same directory together.
+                matches.sort();
+                Ok(matches.join("\n"))
+            }
+        })
     }
 }