MCPcopy Create free account
hub / github.com/atomicdotdev/atomic / search_content

Function search_content

atomic-repository/src/content_search.rs:169–255  ·  view source on GitHub ↗

Search the content index. Returns matching lines with file paths, line numbers, and content. Supports path filtering, file type filtering, and case-insensitive search. Results are ranked: source files (`src/`, `lib/`, `pkg/`, `internal/`) are prioritized over config, test, build, and generated files. Within each tier, results preserve filesystem order from the index. The returned [`ContentSearc

(
    repo_root: &Path,
    pattern: &str,
    opts: ContentSearchOptions,
)

Source from the content-addressed store, hash-verified

167/// (capped to `max_results`) and the `total_matches` count so callers
168/// know how many were found before truncation.
169pub fn search_content(
170 repo_root: &Path,
171 pattern: &str,
172 opts: ContentSearchOptions,
173) -> Result<ContentSearchResult, ContentSearchError> {
174 let config = content_config(repo_root);
175 let index = Index::open(config)?;
176
177 let display_limit = opts.max_results.unwrap_or(50);
178
179 // Fetch a large candidate set so we can rank before truncating.
180 // We ask for 10x the display limit (capped at 2000) to get good
181 // coverage across the repo before applying our own ranking.
182 let fetch_limit = (display_limit * 10).min(2000);
183
184 let search_opts = SearchOptions {
185 path_filter: opts.path_filter,
186 file_type: opts.file_type,
187 exclude_type: opts.exclude_type,
188 max_results: Some(fetch_limit),
189 case_insensitive: opts.case_insensitive,
190 };
191
192 let raw_matches = index.search(pattern, &search_opts)?;
193
194 // Drop matches in ignored or Atomic-internal paths. syntext's index walk
195 // respects `.gitignore`/`.ignore` but NOT `.atomicignore`, and it descends
196 // into hidden dirs like `.atomic`/`.vault`, so build artifacts and
197 // dependencies excluded only by `.atomicignore` (plus Atomic internals)
198 // would otherwise surface here and, via the KG search's content-only file
199 // promotion, as `file:` results. Filter at this shared consumption point
200 // so both `query search` and `query code` stay clean regardless of what
201 // the index contains.
202 let ignore_rules = crate::ignore::IgnoreRules::load_for_enrichment(repo_root);
203 let raw_matches: Vec<syntext::SearchMatch> = raw_matches
204 .into_iter()
205 .filter(|m| {
206 !crate::ignore::is_enrichment_internal(&m.path)
207 && !ignore_rules.is_ignored(&m.path, false)
208 })
209 .collect();
210 let total_matches = raw_matches.len();
211
212 // Build per-directory match counts for the facets summary.
213 let mut dir_counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
214 for m in &raw_matches {
215 let path_str = m.path.to_string_lossy();
216 // Use the first two path components as the directory bucket.
217 let dir = path_str
218 .splitn(3, '/')
219 .take(2)
220 .collect::<Vec<_>>()
221 .join("/");
222 *dir_counts.entry(dir).or_insert(0) += 1;
223 }
224
225 // Sort directory facets by count (descending).
226 let mut dir_facets: Vec<(String, usize)> = dir_counts.into_iter().collect();

Calls 9

content_configFunction · 0.85
is_enrichment_internalFunction · 0.85
truncateMethod · 0.80
as_refMethod · 0.80
path_tierFunction · 0.70
into_iterMethod · 0.45
is_ignoredMethod · 0.45
lenMethod · 0.45
iterMethod · 0.45