syntext 1.0.3

Hybrid code search index for agent workflows
Documentation
//! Count-matches renderer: prints exact per-file match counts.

use std::collections::BTreeSet;
use std::io::{self, Write};
use std::path::PathBuf;

use crate::path_util::path_bytes;
use crate::search::lines::for_each_line;
use crate::Config;

use super::{compile_output_regex, read_repo_file_bytes};
use crate::cli::search::SearchArgs;

pub(in crate::cli) fn render_count_matches(
    config: &Config,
    matches: &[crate::SearchMatch],
    args: &SearchArgs,
) -> io::Result<i32> {
    let re = match compile_output_regex(args) {
        Ok(r) => r,
        Err(e) => {
            eprintln!("st: invalid pattern: {e}");
            return Ok(2);
        }
    };

    let mut per_file: BTreeSet<PathBuf> = BTreeSet::new();
    for m in matches {
        per_file.insert(m.path.clone());
    }

    let stdout = io::stdout();
    let mut out = stdout.lock();
    let mut found_any = false;
    for path in &per_file {
        let raw_bytes = match read_repo_file_bytes(config, path) {
            Ok(b) => b,
            Err(_) => continue,
        };
        let file_bytes = crate::index::normalize_encoding(&raw_bytes, config.verbose);

        let mut count = 0usize;
        for_each_line(file_bytes.as_ref(), |_, _, line| {
            count += re.find_iter(line).count();
        });
        if count == 0 {
            continue;
        }
        found_any = true;
        if args.no_filename {
            writeln!(out, "{count}")?;
        } else {
            out.write_all(path_bytes(path).as_ref())?;
            writeln!(out, ":{count}")?;
        }
    }

    Ok(if found_any { 0 } else { 1 })
}