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