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 if let Some((file_path, line_number)) = file_locations.first() {
130 match self
131 .get_definition_context(file_path, *line_number, 10, Some(symbol))
132 .await
133 {
134 Ok(context) => Ok(context),
135 Err(e) => {
136 error!("Could not fetch context: {e}");
137 Ok(String::new())
138 }
139 }
140 } else {
141 error!("No definition found for symbol '{symbol}'");
142 Ok(String::new())
143 }
144 }
145}