use std::fmt::Write; use std::sync::Arc; use super::symbol_locator::{LocationDisplay, SymbolLocator}; use crate::{AgentTool, ToolCallEventStream, ToolInput}; use agent_client_protocol::schema::v1 as acp; use gpui::{App, Entity, SharedString, Task}; use project::Project; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; /// Finds all references to a symbol across the project using the language server. /// /// Returns a list of locations where the symbol is referenced, including file paths, line numbers, and code snippets for each reference. /// /// Before using this tool, use read_file or grep to find the exact symbol name and line number. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] pub struct FindReferencesToolInput { /// The symbol to find references of. pub symbol: SymbolLocator, } pub struct FindReferencesTool { project: Entity, } impl FindReferencesTool { pub fn new(project: Entity) -> Self { Self { project } } } impl AgentTool for FindReferencesTool { type Input = FindReferencesToolInput; type Output = String; const NAME: &'static str = "find_references"; fn kind() -> acp::ToolKind { acp::ToolKind::Search } fn initial_title( &self, input: Result, _cx: &mut App, ) -> SharedString { if let Ok(input) = input { format!("Find references to `{}`", input.symbol.symbol_name).into() } else { "Find references".into() } } fn run( self: Arc, input: ToolInput, _event_stream: ToolCallEventStream, cx: &mut App, ) -> Task> { let project = self.project.clone(); cx.spawn(async move |cx| { let input = input .recv() .await .map_err(|e| format!("Failed to receive tool input: {e}"))?; let resolved = input.symbol.resolve(&project, cx).await?; let references_task = project.update(cx, |project, cx| { project.references(&resolved.buffer, resolved.position, cx) }); let references = references_task .await .map_err(|e| format!("Find references failed: {e}"))? .unwrap_or_default(); if references.is_empty() { return Ok(format!( "No references found for '{}'.", input.symbol.symbol_name )); } let mut output = format!( "Found {} references to `{}`:\n", references.len(), input.symbol.symbol_name ); for location in &references { let display = location .buffer .read_with(cx, |_, cx| LocationDisplay::from_location(location, cx)); write!(output, "\n## {display}\n").ok(); } Ok(output) }) } }