1use crate::types::Line;
2
3pub fn extract_complete_method(lines: &[&str], start_line: usize) -> (usize, Vec<String>) {
4 let start_idx = start_line.saturating_sub(1);
5 if start_idx >= lines.len() {
6 return (
7 start_line,
8 vec![lines.get(start_idx).unwrap_or(&"").to_string()],
9 );
10 }
11
12 let start_line_content = lines[start_idx];
13
14 let looks_like_function = (start_line_content.contains("(")
15 && (start_line_content.contains("{")
16 || start_line_content.trim_end().ends_with(")")
17 || start_line_content.trim_end().ends_with(";")
18 || start_line_content.contains("::")
19 || start_line_content.trim_start().starts_with("fn ")
20 || start_line_content.contains("function ")))
21 || start_line_content.contains("class ")
22 || start_line_content.contains("struct ")
23 || start_line_content.contains("interface ");
24
25 if !looks_like_function {
26 let mut found_function_pattern = false;
27 for i in 0..=5.min(lines.len().saturating_sub(start_idx + 1)) {
28 if let Some(line) = lines.get(start_idx + i) {
29 if line.contains("{") || line.trim_start().starts_with(":") {
30 found_function_pattern = true;
31 break;
32 }
33 }
34 }
35
36 if !found_function_pattern {
37 let context_start = start_idx.saturating_sub(5);
38 let context_end = std::cmp::min(start_idx + 5, lines.len());
39 let context_lines: Vec<String> = lines[context_start..context_end]
40 .iter()
41 .enumerate()
42 .map(|(i, line)| {
43 let line_num = context_start + i + 1;
44 let marker = if line_num == start_line { ">>>" } else { " " };
45 format!("{marker} {line_num:4}: {line}")
46 })
47 .collect();
48 return (start_line, context_lines);
49 }
50 }
51
52 if start_line_content.trim_end().ends_with(';')
53 && !(start_line_content.contains("class ") || start_line_content.contains("struct "))
54 {
55 return (
56 start_line,
57 vec![format!(">>> {:4}: {}", start_line, start_line_content)],
58 );
59 }
60
61 let mut found_opening_brace = start_line_content.contains('{');
62
63 if !found_opening_brace {
64 for (i, line) in lines.iter().enumerate().skip(start_idx + 1) {
65 if line.contains('{') {
66 found_opening_brace = true;
67 break;
68 }
69 if i > start_idx + 25
70 || (line.trim().is_empty() && i > start_idx + 5)
71 || (line.contains("::")
72 && line.contains("(")
73 && !line.trim_start().starts_with("//")
74 && !line.contains("mId")
75 && !line.contains("m"))
76 {
77 break;
78 }
79 }
80 }
81
82 if !found_opening_brace {
83 return (
84 start_line,
85 vec![format!(">>> {:4}: {}", start_line, start_line_content)],
86 );
87 }
88
89 let mut result_lines = Vec::new();
90 let mut brace_count = 0;
91 let mut in_string = false;
92 let mut in_char = false;
93 let mut escaped = false;
94 let mut in_single_comment = false;
95 let mut in_multi_comment = false;
96
97 for (i, line) in lines.iter().enumerate().skip(start_idx) {
98 let line_num = i + 1;
99 let marker = if line_num == start_line { ">>>" } else { " " };
100 result_lines.push(format!("{marker} {line_num:4}: {line}"));
101
102 let chars: Vec<char> = line.chars().collect();
103 let mut j = 0;
104 while j < chars.len() {
105 let ch = chars[j];
106 let next_ch = chars.get(j + 1).copied();
107
108 if escaped {
109 escaped = false;
110 j += 1;
111 continue;
112 }
113
114 match ch {
115 '\\' if in_string || in_char => escaped = true,
116 '"' if !in_char && !in_single_comment && !in_multi_comment => {
117 in_string = !in_string
118 }
119 '\'' if !in_string && !in_single_comment && !in_multi_comment => in_char = !in_char,
120 '/' if !in_string && !in_char && !in_single_comment && !in_multi_comment => {
121 if next_ch == Some('/') {
122 in_single_comment = true;
123 j += 1;
124 } else if next_ch == Some('*') {
125 in_multi_comment = true;
126 j += 1;
127 }
128 }
129 '*' if in_multi_comment && next_ch == Some('/') => {
130 in_multi_comment = false;
131 j += 1;
132 }
133 '{' if !in_string && !in_char && !in_single_comment && !in_multi_comment => {
134 brace_count += 1;
135 }
136 '}' if !in_string && !in_char && !in_single_comment && !in_multi_comment => {
137 brace_count -= 1;
138 if brace_count == 0 {
139 let is_class_or_struct = lines[start_idx].contains("class ")
140 || lines[start_idx].contains("struct ");
141 if is_class_or_struct {
142 let remaining_on_line = &line[j + 1..];
143 if remaining_on_line.trim().starts_with(';') {
144 return (start_line, result_lines);
145 } else if i + 1 < lines.len() {
146 let next_line = lines[i + 1];
147 if next_line.trim().starts_with(';') {
148 result_lines.push(format!(" {:4}: {}", i + 2, next_line));
149 }
150 }
151 }
152 return (start_line, result_lines);
153 }
154 }
155 _ => {}
156 }
157 j += 1;
158 }
159
160 in_single_comment = false;
161
162 if result_lines.len() > 200 {
163 result_lines.push(" ... : (method too long, truncated)".to_string());
164 break;
165 }
166 }
167
168 (start_line, result_lines)
169}
170
171pub fn is_potential_definition(line: &Line, query: &str) -> bool {
172 let line_text = &line.line;
173 let line_lower = line_text.to_lowercase();
174 let query_lower = query.to_lowercase();
175
176 let is_constructor = if let Some(colon_pos) = query.rfind("::") {
177 let class_part = &query[..colon_pos];
178 let method_part = &query[colon_pos + 2..];
179 let class_name = class_part.split("::").last().unwrap_or(class_part);
180 if class_name != method_part {
181 return false;
182 }
183 line_text.contains(&format!("::{}(", method_part))
184 || (line_text
185 .trim_start()
186 .starts_with(&format!("{}(", method_part))
187 && !line_text.contains("return"))
188 } else {
189 false
190 };
191
192 let contains_query =
193 line_text.contains(query) || line_lower.contains(&query_lower) || is_constructor;
194
195 if contains_query {
196 let looks_like_definition = line_text.contains("{")
197 || line_text.trim_end().ends_with(';')
198 || line_text.contains("=")
199 || line_text.contains("class ")
200 || line_text.contains("struct ")
201 || line_text.contains("interface ")
202 || is_constructor
203 || (line_text.contains("::")
204 && (line_text.contains("(")
205 || line_text.contains("already_AddRefed")
206 || line_text.contains("RefPtr")
207 || line_text.contains("nsCOMPtr")));
208
209 looks_like_definition
210 } else {
211 false
212 }
213}
214
215pub fn searchfox_url_repo(repo: &str) -> &str {
216 if std::env::var("SEARCHFOX_BASE_URL").is_ok() {
219 return repo;
220 }
221 match repo {
222 "mozilla-central" => "firefox-main",
223 "mozilla-beta" => "firefox-beta",
224 "mozilla-release" => "firefox-release",
225 "mozilla-esr128" => "firefox-esr128",
226 "mozilla-esr140" => "firefox-esr140",
227 _ => repo,
228 }
229}