searchfox_lib/
definition.rs1use crate::client::SearchfoxClient;
2use crate::search::SearchOptions;
3use crate::utils::{
4 extract_complete_method, find_symbol_in_local_content, is_mozilla_repository, read_local_file,
5};
6use anyhow::Result;
7use log::{debug, error};
8
9impl SearchfoxClient {
10 pub async fn get_definition_context(
11 &self,
12 file_path: &str,
13 line_number: usize,
14 context_lines: usize,
15 symbol_name: Option<&str>,
16 ) -> Result<String> {
17 if is_mozilla_repository() {
18 if let Some(local_content) = read_local_file(file_path) {
19 let lines: Vec<&str> = local_content.lines().collect();
20
21 let actual_line = if line_number > 0 && line_number <= lines.len() {
22 let line_idx = line_number - 1;
23 let line_content = lines[line_idx];
24
25 let looks_correct = if let Some(symbol) = symbol_name {
26 line_content.contains(symbol)
27 || (symbol.contains("::")
28 && line_content.contains(symbol.split("::").last().unwrap_or("")))
29 } else {
30 line_content.contains("::") || line_content.contains("(")
31 };
32
33 if looks_correct {
34 Some(line_number)
35 } else if let Some(symbol) = symbol_name {
36 find_symbol_in_local_content(&local_content, line_number, symbol)
37 } else {
38 None
39 }
40 } else if let Some(symbol) = symbol_name {
41 find_symbol_in_local_content(&local_content, 1, symbol)
42 } else {
43 None
44 };
45
46 let final_line = actual_line.unwrap_or(line_number);
47
48 let (_, method_lines) = extract_complete_method(&lines, final_line);
49
50 if method_lines.len() > 1 {
51 return Ok(method_lines.join("\n"));
52 }
53
54 let start_line = if final_line > context_lines {
55 final_line - context_lines
56 } else {
57 1
58 };
59 let end_line = std::cmp::min(final_line + context_lines, lines.len());
60
61 let mut result = String::new();
62 for (i, line) in lines.iter().enumerate() {
63 let line_num = i + 1;
64 if line_num >= start_line && line_num <= end_line {
65 let marker = if line_num == final_line { ">>>" } else { " " };
66 result.push_str(&format!("{marker} {line_num:4}: {line}\n"));
67 }
68 }
69
70 return Ok(result);
71 }
72 }
73
74 let file_content = self.get_file(file_path).await?;
75 let lines: Vec<&str> = file_content.lines().collect();
76
77 let (_, method_lines) = extract_complete_method(&lines, line_number);
78
79 if method_lines.len() > 1 {
80 return Ok(method_lines.join("\n"));
81 }
82
83 let start_line = if line_number > context_lines {
84 line_number - context_lines
85 } else {
86 1
87 };
88 let end_line = std::cmp::min(line_number + context_lines, lines.len());
89
90 let mut result = String::new();
91 for (i, line) in lines.iter().enumerate() {
92 let line_num = i + 1;
93 if line_num >= start_line && line_num <= end_line {
94 let marker = if line_num == line_number {
95 ">>>"
96 } else {
97 " "
98 };
99 result.push_str(&format!("{marker} {line_num:4}: {line}\n"));
100 }
101 }
102
103 Ok(result)
104 }
105
106 pub async fn find_and_display_definition(
107 &self,
108 symbol: &str,
109 path_filter: Option<&str>,
110 options: &SearchOptions,
111 ) -> Result<String> {
112 debug!("Finding potential definition locations...");
113 let file_locations = self
114 .find_symbol_locations(symbol, path_filter, options)
115 .await?;
116
117 if file_locations.is_empty() {
118 error!("No potential definitions found for '{symbol}'");
119 return Ok(String::new());
120 }
121
122 debug!(
123 "Found {} potential definition location(s)",
124 file_locations.len()
125 );
126
127 let is_ctor = symbol.rfind("::").is_some_and(|pos| {
128 let class_part = &symbol[..pos];
129 let method_part = &symbol[pos + 2..];
130 let class_name = class_part.split("::").last().unwrap_or(class_part);
131 class_name == method_part
132 });
133
134 let mut results = Vec::new();
135 for (file_path, line_number) in &file_locations {
136 let context_lines = if is_ctor { 2 } else { 10 };
137 match self
138 .get_definition_context(file_path, *line_number, context_lines, Some(symbol))
139 .await
140 {
141 Ok(context) => {
142 if !context.is_empty() {
143 results.push(context);
144 }
145 }
146 Err(e) => {
147 error!("Could not fetch context: {e}");
148 }
149 }
150 }
151
152 if results.is_empty() {
153 error!("No definition found for symbol '{symbol}'");
154 Ok(String::new())
155 } else if results.len() == 1 {
156 Ok(results[0].clone())
157 } else {
158 Ok(results.join("\n\n"))
159 }
160 }
161}