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

Function search_content

atomic-repository/src/content_search.rs:153–239  ·  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

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