project: Fix search filter patterns on remote projects (#34748)

maan2003 created

we were join(",") and split(",") to serialize the patterns.

This doesn't work when pattern includes a ","
example: *.{ts,tsx} (very common pattern used by agent)


help needed:

how will this work on version mismatch?

Release Notes:

- Fixed search filter patterns on remote projects.

Change summary

crates/project/src/search.rs    | 49 +++++++++++++++++++++++++----------
crates/proto/proto/buffer.proto |  6 ++-
2 files changed, 39 insertions(+), 16 deletions(-)

Detailed changes

crates/project/src/search.rs 🔗

@@ -193,6 +193,30 @@ impl SearchQuery {
     }
 
     pub fn from_proto(message: proto::SearchQuery) -> Result<Self> {
+        let files_to_include = if message.files_to_include.is_empty() {
+            message
+                .files_to_include_legacy
+                .split(',')
+                .map(str::trim)
+                .filter(|&glob_str| !glob_str.is_empty())
+                .map(|s| s.to_string())
+                .collect()
+        } else {
+            message.files_to_include
+        };
+
+        let files_to_exclude = if message.files_to_exclude.is_empty() {
+            message
+                .files_to_exclude_legacy
+                .split(',')
+                .map(str::trim)
+                .filter(|&glob_str| !glob_str.is_empty())
+                .map(|s| s.to_string())
+                .collect()
+        } else {
+            message.files_to_exclude
+        };
+
         if message.regex {
             Self::regex(
                 message.query,
@@ -200,8 +224,8 @@ impl SearchQuery {
                 message.case_sensitive,
                 message.include_ignored,
                 false,
-                deserialize_path_matches(&message.files_to_include)?,
-                deserialize_path_matches(&message.files_to_exclude)?,
+                PathMatcher::new(files_to_include)?,
+                PathMatcher::new(files_to_exclude)?,
                 message.match_full_paths,
                 None, // search opened only don't need search remote
             )
@@ -211,8 +235,8 @@ impl SearchQuery {
                 message.whole_word,
                 message.case_sensitive,
                 message.include_ignored,
-                deserialize_path_matches(&message.files_to_include)?,
-                deserialize_path_matches(&message.files_to_exclude)?,
+                PathMatcher::new(files_to_include)?,
+                PathMatcher::new(files_to_exclude)?,
                 false,
                 None, // search opened only don't need search remote
             )
@@ -236,15 +260,20 @@ impl SearchQuery {
     }
 
     pub fn to_proto(&self) -> proto::SearchQuery {
+        let files_to_include = self.files_to_include().sources().to_vec();
+        let files_to_exclude = self.files_to_exclude().sources().to_vec();
         proto::SearchQuery {
             query: self.as_str().to_string(),
             regex: self.is_regex(),
             whole_word: self.whole_word(),
             case_sensitive: self.case_sensitive(),
             include_ignored: self.include_ignored(),
-            files_to_include: self.files_to_include().sources().join(","),
-            files_to_exclude: self.files_to_exclude().sources().join(","),
+            files_to_include: files_to_include.clone(),
+            files_to_exclude: files_to_exclude.clone(),
             match_full_paths: self.match_full_paths(),
+            // Populate legacy fields for backwards compatibility
+            files_to_include_legacy: files_to_include.join(","),
+            files_to_exclude_legacy: files_to_exclude.join(","),
         }
     }
 
@@ -520,14 +549,6 @@ impl SearchQuery {
     }
 }
 
-pub fn deserialize_path_matches(glob_set: &str) -> anyhow::Result<PathMatcher> {
-    let globs = glob_set
-        .split(',')
-        .map(str::trim)
-        .filter(|&glob_str| !glob_str.is_empty());
-    Ok(PathMatcher::new(globs)?)
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;

crates/proto/proto/buffer.proto 🔗

@@ -288,10 +288,12 @@ message SearchQuery {
     bool regex = 3;
     bool whole_word = 4;
     bool case_sensitive = 5;
-    string files_to_include = 6;
-    string files_to_exclude = 7;
+    repeated string files_to_include = 10;
+    repeated string files_to_exclude = 11;
     bool match_full_paths = 9;
     bool include_ignored = 8;
+    string files_to_include_legacy = 6;
+    string files_to_exclude_legacy = 7;
 }
 
 message FindSearchCandidates {