vv-agent 0.6.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
mod command;
mod parse;
mod paths;
#[cfg(all(test, unix))]
mod tests;
mod types;

use std::process::Command;

use crate::tools::common::{command_output_with_executable_busy_retry, sensitive_rg_exclude_globs};

use command::rg_file_type_globs;
use parse::parse_rg_json_output;
use paths::local_ignored_root_names;

pub(super) use command::resolve_rg_executable;
pub(super) use paths::is_workspace_root_path;
pub(super) use types::{RgGrepResult, RgSearchFilesRequest};

pub(super) fn search_files_local_rg(request: RgSearchFilesRequest<'_>) -> Option<RgGrepResult> {
    let RgSearchFilesRequest {
        context,
        path,
        glob_pattern,
        pattern,
        output_mode,
        file_type,
        case_insensitive,
        literal,
        multiline,
        before_context,
        after_context,
        include_hidden,
        include_ignored,
        include_sensitive,
        rg_executable,
    } = request;

    let base_path = context.resolve_workspace_path(path).ok()?;
    if !base_path.exists() || !base_path.is_dir() {
        return None;
    }

    let base_is_workspace_root = is_workspace_root_path(path);
    let ignored_root_names = if base_is_workspace_root && !include_ignored {
        local_ignored_root_names(&base_path)
    } else {
        Vec::new()
    };

    let mut command = Command::new(rg_executable);
    command
        .arg("--json")
        .arg("--line-number")
        .arg("--color")
        .arg("never")
        .arg("--no-messages");
    if include_hidden {
        command.arg("--hidden");
    }
    if include_ignored {
        command.arg("--no-ignore").arg("--no-ignore-vcs");
    }
    if case_insensitive {
        command.arg("-i");
    }
    if literal {
        command.arg("--fixed-strings");
    }
    if multiline {
        command.arg("--multiline").arg("--multiline-dotall");
    }
    if before_context > 0 {
        command
            .arg("--before-context")
            .arg(before_context.to_string());
    }
    if after_context > 0 {
        command
            .arg("--after-context")
            .arg(after_context.to_string());
    }
    if !glob_pattern.trim().is_empty() && glob_pattern != "**/*" {
        command.arg("--glob").arg(glob_pattern);
    }
    if !include_sensitive {
        for sensitive_glob in sensitive_rg_exclude_globs() {
            command.arg("--glob").arg(sensitive_glob);
        }
    }
    if base_is_workspace_root && !include_ignored {
        for root in &ignored_root_names {
            command.arg("--glob").arg(format!("!{root}/**"));
        }
    }
    if let Some(file_type) = file_type {
        for file_glob in rg_file_type_globs(file_type) {
            command.arg("--iglob").arg(file_glob);
        }
    }
    let output = command_output_with_executable_busy_retry(
        command
            .arg("--regexp")
            .arg(pattern)
            .arg(".")
            .current_dir(&base_path),
    )
    .ok()?;
    if !matches!(output.status.code(), Some(0) | Some(1) | Some(2)) {
        return None;
    }

    parse_rg_json_output(
        context,
        &base_path,
        output_mode,
        file_type,
        multiline,
        &output.stdout,
    )
}