| 163 | } |
| 164 | |
| 165 | async fn search_simple( |
| 166 | &self, |
| 167 | query: &str, |
| 168 | files: &[IndexedFile], |
| 169 | max_results: usize, |
| 170 | ) -> Vec<(IndexedFile, f32)> { |
| 171 | let query_lower = query.to_lowercase(); |
| 172 | let keywords: Vec<&str> = query_lower.split_whitespace().collect(); |
| 173 | |
| 174 | let mut results: Vec<(IndexedFile, f32)> = files |
| 175 | .iter() |
| 176 | .filter_map(|file| { |
| 177 | let content_lower = file.content.to_lowercase(); |
| 178 | let mut score = 0.0; |
| 179 | for keyword in &keywords { |
| 180 | let count = content_lower.matches(keyword).count(); |
| 181 | score += count as f32; |
| 182 | } |
| 183 | |
| 184 | if score > 0.0 { |
| 185 | let normalized_score = score / (file.content.len() as f32).sqrt(); |
| 186 | Some((file.clone(), normalized_score)) |
| 187 | } else { |
| 188 | None |
| 189 | } |
| 190 | }) |
| 191 | .collect(); |
| 192 | |
| 193 | results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); |
| 194 | results.truncate(max_results); |
| 195 | results |
| 196 | } |
| 197 | |
| 198 | async fn update_cache(&self, files: Vec<IndexedFile>) { |
| 199 | if !self.config.enable_cache { |