Skip to main content

vtcode_commons/
walk.rs

1//! Shared directory walker helpers built on the `ignore` crate.
2//!
3//! All file traversal in vtcode should go through these builders so that
4//! `.gitignore`, `.ignore`, `.git/exclude`, and the centralized exclusion
5//! constants are applied consistently.
6
7use ignore::{DirEntry, WalkBuilder};
8use std::path::Path;
9
10use crate::exclusions::DEFAULT_EXCLUDED_DIRS;
11
12/// Build a multi-threaded [`WalkBuilder`] with sensible defaults.
13///
14/// - Respects `.gitignore`, `.ignore`, `.git/exclude`, and parent ignore files
15/// - Does not follow symlinks
16/// - Uses the `ignore` crate's default thread pool
17///
18/// Callers that need to prune additional directories should use
19/// [`filter_entry`](WalkBuilder::filter_entry) with [`is_excluded_dir`].
20pub fn build_default_walker(root: &Path) -> WalkBuilder {
21    let mut builder = WalkBuilder::new(root);
22    apply_defaults(&mut builder);
23    builder
24}
25
26/// Build a single-threaded [`WalkBuilder`] with the same defaults as
27/// [`build_default_walker`].
28///
29/// Use this in synchronous contexts where spawning the `ignore` crate's
30/// thread pool would be wasteful (e.g., inside `spawn_blocking` closures
31/// that already run on a dedicated thread).
32pub fn build_walker_single_threaded(root: &Path) -> WalkBuilder {
33    let mut builder = WalkBuilder::new(root);
34    builder.threads(1);
35    apply_defaults(&mut builder);
36    builder
37}
38
39/// Apply standard walker defaults to an existing [`WalkBuilder`].
40///
41/// Sets gitignore support, hidden file visibility, and symlink policy.
42/// Callers that need additional customization (e.g., parallel walkers,
43/// symlink following) can call this then override specific settings.
44pub fn apply_defaults(builder: &mut WalkBuilder) {
45    // Respect all standard ignore-file mechanisms.
46    builder.git_ignore(true);
47    builder.git_global(true);
48    builder.git_exclude(true);
49    builder.ignore(true);
50    builder.parents(true);
51
52    // Do not follow symlinks by default.
53    builder.follow_links(false);
54
55    // Do not skip hidden files by default.  The `ignore` crate skips them
56    // by default, but the previous traversal code did not.  Callers that
57    // want to hide dotfiles should filter them explicitly.
58    builder.hidden(false);
59}
60
61/// Returns `true` if `entry` is a directory whose name appears in
62/// [`DEFAULT_EXCLUDED_DIRS`].
63///
64/// Intended for use inside [`WalkBuilder::filter_entry`] closures:
65///
66/// ```ignore
67/// builder.filter_entry(|entry| !vtcode_commons::walk::is_excluded_dir(entry));
68/// ```
69pub fn is_excluded_dir(entry: &DirEntry) -> bool {
70    if !entry.file_type().is_some_and(|ft| ft.is_dir()) {
71        return false;
72    }
73
74    entry
75        .file_name()
76        .to_str()
77        .is_some_and(|name| DEFAULT_EXCLUDED_DIRS.contains(&name))
78}