Skip to content

Commit 6f2dfa7

Browse files
karthiknadigCopilot
andcommitted
feat: improve recursive environment glob diagnostics (Fixes #427)
Warn before expanding recursive environmentDirectories and report exact per-pattern timings for slow configure globs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b618197 commit 6f2dfa7

3 files changed

Lines changed: 67 additions & 27 deletions

File tree

‎crates/pet-fs/src/glob.rs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ pub fn is_glob_pattern(path: &str) -> bool {
1919
path.contains(GLOB_METACHARACTERS) || has_brace_pattern(path)
2020
}
2121

22+
/// Returns true when a glob can traverse an unbounded number of path components.
23+
pub fn is_recursive_glob_pattern(path: &str) -> bool {
24+
path.contains("**")
25+
}
2226
/// Checks if a string contains a valid brace expansion pattern `{a,b}`.
2327
/// Requires an opening `{`, at least one `,`, and a closing `}`.
2428
fn has_brace_pattern(path: &str) -> bool {
@@ -202,6 +206,13 @@ mod tests {
202206
assert!(is_glob_pattern("*.txt"));
203207
}
204208

209+
#[test]
210+
fn test_is_recursive_glob_pattern() {
211+
assert!(is_recursive_glob_pattern("**/.venv"));
212+
assert!(is_recursive_glob_pattern("/home/user/**/venv"));
213+
assert!(!is_recursive_glob_pattern(".venv"));
214+
assert!(!is_recursive_glob_pattern("*/.venv"));
215+
}
205216
#[test]
206217
fn test_is_glob_pattern_with_question_mark() {
207218
assert!(is_glob_pattern("/home/user/file?.txt"));

‎crates/pet/src/jsonrpc.rs‎

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use pet_core::{
2121
Configuration, Locator, RefreshStatePersistence, RefreshStateSyncScope,
2222
};
2323
use pet_env_var_path::get_search_paths_from_env_variables;
24-
use pet_fs::glob::expand_glob_patterns;
24+
use pet_fs::glob::{expand_glob_pattern, expand_glob_patterns, is_recursive_glob_pattern};
2525
use pet_fs::path::norm_case;
2626
use pet_jsonrpc::{
2727
send_error, send_reply,
@@ -566,6 +566,43 @@ pub struct ConfigureOptions {
566566
/// The client has a 30-second timeout for configure requests.
567567
const GLOB_EXPANSION_WARN_THRESHOLD: Duration = Duration::from_secs(5);
568568

569+
fn expand_configure_directory_patterns(kind: &str, patterns: Vec<PathBuf>) -> Vec<PathBuf> {
570+
patterns
571+
.into_iter()
572+
.flat_map(|pattern| {
573+
let start = Instant::now();
574+
let expanded = expand_glob_pattern(&pattern.to_string_lossy());
575+
let elapsed = start.elapsed();
576+
trace!(
577+
"Expanded {} pattern '{}' in {:?}",
578+
kind,
579+
pattern.display(),
580+
elapsed
581+
);
582+
if elapsed >= GLOB_EXPANSION_WARN_THRESHOLD {
583+
warn!(
584+
"Expanding {} pattern '{}' took {:?}, this may cause client timeouts",
585+
kind,
586+
pattern.display(),
587+
elapsed
588+
);
589+
}
590+
expanded
591+
})
592+
.filter(|path| path.is_dir())
593+
.collect()
594+
}
595+
596+
fn warn_for_recursive_environment_patterns(patterns: &[PathBuf]) {
597+
for pattern in patterns {
598+
if is_recursive_glob_pattern(&pattern.to_string_lossy()) {
599+
warn!(
600+
"Recursive environmentDirectories pattern '{}' can make configure slow; prefer bounded patterns such as '.venv' or '*/.venv'",
601+
pattern.display()
602+
);
603+
}
604+
}
605+
}
569606
pub fn handle_configure(context: Arc<Context>, id: u32, params: Value) {
570607
match serde_json::from_value::<ConfigureOptions>(params.clone()) {
571608
Ok(mut configure_options) => {
@@ -574,38 +611,28 @@ pub fn handle_configure(context: Arc<Context>, id: u32, params: Value) {
574611
thread::spawn(move || {
575612
let now = Instant::now();
576613

614+
// Warn before any expansion so a slow workspace pattern cannot delay
615+
// the actionable environmentDirectories diagnostic.
616+
if let Some(patterns) = configure_options.environment_directories.as_deref() {
617+
warn_for_recursive_environment_patterns(patterns);
618+
}
619+
577620
// Expand glob patterns before acquiring the write lock so we
578621
// don't block readers/writers while traversing the filesystem.
579622
let workspace_directories =
580-
configure_options.workspace_directories.take().map(|dirs| {
581-
let start = Instant::now();
582-
let result: Vec<PathBuf> = expand_glob_patterns(&dirs)
583-
.into_iter()
584-
.filter(|p| p.is_dir())
585-
.collect();
586-
trace!(
587-
"Expanded workspace directory patterns ({:?}) in {:?}",
588-
dirs,
589-
start.elapsed()
590-
);
591-
result
592-
});
623+
configure_options
624+
.workspace_directories
625+
.take()
626+
.map(|patterns| {
627+
expand_configure_directory_patterns("workspaceDirectories", patterns)
628+
});
593629
let environment_directories =
594630
configure_options
595631
.environment_directories
596632
.take()
597-
.map(|dirs| {
598-
let start = Instant::now();
599-
let result: Vec<PathBuf> = expand_glob_patterns(&dirs)
600-
.into_iter()
601-
.filter(|p| p.is_dir())
602-
.collect();
603-
trace!(
604-
"Expanded environment directory patterns ({:?}) in {:?}",
605-
dirs,
606-
start.elapsed()
607-
);
608-
result
633+
.map(|patterns| {
634+
warn_for_recursive_environment_patterns(&patterns);
635+
expand_configure_directory_patterns("environmentDirectories", patterns)
609636
});
610637
let glob_elapsed = now.elapsed();
611638
trace!("Glob expansion completed in {:?}", glob_elapsed);

‎docs/JSONRPC.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ interface ConfigureParams {
8383
*
8484
* Useful for VS Code so users can configure where they store virtual environments.
8585
*
86-
* Glob patterns are supported (e.g., "/home/user/envs/*", "/home/user/*/venv").
86+
* Bounded glob patterns are supported (e.g., ".venv", "*/.venv", "/home/user/envs/*").
87+
* Avoid recursive workspace-wide patterns such as "**/.venv": they can traverse large directory trees,
88+
* delay configure responses, and trigger client timeouts.
8789
*/
8890
environmentDirectories?: string[];
8991
/**

0 commit comments

Comments
 (0)