agent_ui: Make rust-analyzer paths work with symbol completions (#46739)

Lukas Wirth created

Release Notes:

- N/A *or* Added/Fixed/Improved ...

Change summary

crates/agent_ui/Cargo.toml                    | 15 ++++++++++++
crates/agent_ui/src/completion_provider.rs    | 23 ++++++++++++++++++++
crates/project_symbols/src/project_symbols.rs |  1 
3 files changed, 37 insertions(+), 2 deletions(-)

Detailed changes

crates/agent_ui/Cargo.toml 🔗

@@ -13,7 +13,18 @@ path = "src/agent_ui.rs"
 doctest = false
 
 [features]
-test-support = ["assistant_text_thread/test-support", "acp_thread/test-support", "eval_utils", "gpui/test-support", "language/test-support", "reqwest_client", "workspace/test-support", "agent/test-support", "rules_library/test-support"]
+test-support = [
+    "assistant_text_thread/test-support",
+    "acp_thread/test-support",
+    "eval_utils",
+    "gpui/test-support",
+    "git_ui/test-support",
+    "language/test-support",
+    "reqwest_client",
+    "workspace/test-support",
+    "agent/test-support",
+    "rules_library/test-support"
+]
 unit-eval = []
 
 [dependencies]
@@ -116,6 +127,8 @@ db = { workspace = true, features = ["test-support"] }
 editor = { workspace = true, features = ["test-support"] }
 eval_utils.workspace = true
 gpui = { workspace = true, "features" = ["test-support"] }
+git_ui = { workspace = true, features = ["test-support"] }
+rules_library = { workspace = true, features = ["test-support"] }
 indoc.workspace = true
 language = { workspace = true, "features" = ["test-support"] }
 languages = { workspace = true, features = ["test-support"] }

crates/agent_ui/src/completion_provider.rs 🔗

@@ -1556,7 +1556,15 @@ pub(crate) fn search_symbols(
                         SymbolLocation::OutsideProject { .. } => false,
                     })
             });
-
+        // Try to support rust-analyzer's path based symbols feature which
+        // allows to search by rust path syntax, in that case we only want to
+        // filter names by the last segment
+        // Ideally this was a first class LSP feature (rich queries)
+        let query = query
+            .rsplit_once("::")
+            .map_or(&*query, |(_, suffix)| suffix)
+            .to_owned();
+        // Note if you make changes to this filtering below, also change `project_symbols::ProjectSymbolsDelegate::filter`
         const MAX_MATCHES: usize = 100;
         let mut visible_matches = cx.background_executor().block(fuzzy::match_strings(
             &visible_match_candidates,
@@ -2012,6 +2020,19 @@ mod tests {
             })
         );
 
+        assert_eq!(
+            MentionCompletion::try_parse(
+                "Lorem @symbol agent_ui::completion_provider",
+                0,
+                &supported_modes
+            ),
+            Some(MentionCompletion {
+                source_range: 6..43,
+                mode: Some(PromptContextType::Symbol),
+                argument: Some("agent_ui::completion_provider".to_string()),
+            })
+        );
+
         // Disallowed non-file mentions
         assert_eq!(
             MentionCompletion::try_parse("Lorem @symbol main", 0, &[PromptContextType::File]),

crates/project_symbols/src/project_symbols.rs 🔗

@@ -61,6 +61,7 @@ impl ProjectSymbolsDelegate {
         }
     }
 
+    // Note if you make changes to this, also change `agent_ui::completion_provider::search_symbols`
     fn filter(&mut self, query: &str, window: &mut Window, cx: &mut Context<Picker<Self>>) {
         const MAX_MATCHES: usize = 100;
         let mut visible_matches = cx.background_executor().block(fuzzy::match_strings(