diff --git a/Cargo.lock b/Cargo.lock index 7cc3e37f74..018df705ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2439,6 +2439,7 @@ version = "0.2.2" dependencies = [ "anyhow", "crossbeam", + "ignore", "insta", "jod-thread", "libc", @@ -2463,7 +2464,6 @@ dependencies = [ "shellexpand", "tracing", "tracing-subscriber", - "walkdir", ] [[package]] diff --git a/crates/ruff_server/Cargo.toml b/crates/ruff_server/Cargo.toml index 6420703f1f..6f48754969 100644 --- a/crates/ruff_server/Cargo.toml +++ b/crates/ruff_server/Cargo.toml @@ -28,6 +28,7 @@ ruff_workspace = { workspace = true } anyhow = { workspace = true } crossbeam = { workspace = true } +ignore = { workspace = true } jod-thread = { workspace = true } lsp-server = { workspace = true } lsp-types = { workspace = true } @@ -38,7 +39,6 @@ serde_json = { workspace = true } shellexpand = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } -walkdir = { workspace = true } [dev-dependencies] insta = { workspace = true } diff --git a/crates/ruff_server/src/session/index/ruff_settings.rs b/crates/ruff_server/src/session/index/ruff_settings.rs index d8de932604..7791a086f6 100644 --- a/crates/ruff_server/src/session/index/ruff_settings.rs +++ b/crates/ruff_server/src/session/index/ruff_settings.rs @@ -1,3 +1,9 @@ +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use ignore::{WalkBuilder, WalkState}; + use ruff_linter::{ display_settings, fs::normalize_path_to, settings::types::FilePattern, settings::types::PreviewMode, @@ -8,12 +14,6 @@ use ruff_workspace::{ pyproject::{find_user_settings_toml, settings_toml}, resolver::{ConfigurationTransformer, Relativity}, }; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, - sync::Arc, -}; -use walkdir::WalkDir; use crate::session::settings::{ConfigurationPreference, ResolvedEditorSettings}; @@ -30,7 +30,7 @@ pub struct RuffSettings { } pub(super) struct RuffSettingsIndex { - /// Index from folder to the resoled ruff settings. + /// Index from folder to the resolved ruff settings. index: BTreeMap>, fallback: Arc, } @@ -100,6 +100,7 @@ impl RuffSettings { impl RuffSettingsIndex { pub(super) fn new(root: &Path, editor_settings: &ResolvedEditorSettings) -> Self { let mut index = BTreeMap::default(); + let mut respect_gitignore = true; // Add any settings from above the workspace root. for directory in root.ancestors() { @@ -112,6 +113,7 @@ impl RuffSettingsIndex { continue; }; + respect_gitignore = settings.file_resolver.respect_gitignore; index.insert( directory.to_path_buf(), Arc::new(RuffSettings { @@ -126,70 +128,88 @@ impl RuffSettingsIndex { } // Add any settings within the workspace itself - let mut walker = WalkDir::new(root).into_iter(); + let mut builder = WalkBuilder::new(root); + builder.standard_filters(respect_gitignore); + builder.hidden(false); + builder.threads( + std::thread::available_parallelism() + .map_or(1, std::num::NonZeroUsize::get) + .min(12), + ); + let walker = builder.build_parallel(); - while let Some(entry) = walker.next() { - let Ok(entry) = entry else { - continue; - }; + let index = std::sync::RwLock::new(index); + walker.run(|| { + Box::new(|result| { + let Ok(entry) = result else { + return WalkState::Continue; + }; - // Skip non-directories. - if !entry.file_type().is_dir() { - continue; - } - - let directory = entry.into_path(); - - // If the directory is excluded from the workspace, skip it. - if let Some(file_name) = directory.file_name() { - if let Some((_, settings)) = index - .range(..directory.clone()) - .rfind(|(path, _)| directory.starts_with(path)) + // Skip non-directories. + if !entry + .file_type() + .is_some_and(|file_type| file_type.is_dir()) { - if match_exclusion(&directory, file_name, &settings.file_resolver.exclude) { - tracing::debug!("Ignored path via `exclude`: {}", directory.display()); + return WalkState::Continue; + } - walker.skip_current_dir(); - continue; - } else if match_exclusion( - &directory, - file_name, - &settings.file_resolver.extend_exclude, - ) { - tracing::debug!( - "Ignored path via `extend-exclude`: {}", - directory.display() - ); + let directory = entry.into_path(); + tracing::debug!("Visiting: {}", directory.display()); - walker.skip_current_dir(); - continue; + // If the directory is excluded from the workspace, skip it. + if let Some(file_name) = directory.file_name() { + if let Some((_, settings)) = index + .read() + .unwrap() + .range(..directory.clone()) + .rfind(|(path, _)| directory.starts_with(path)) + { + if match_exclusion(&directory, file_name, &settings.file_resolver.exclude) { + tracing::debug!("Ignored path via `exclude`: {}", directory.display()); + return WalkState::Continue; + } else if match_exclusion( + &directory, + file_name, + &settings.file_resolver.extend_exclude, + ) { + tracing::debug!( + "Ignored path via `extend-exclude`: {}", + directory.display() + ); + return WalkState::Continue; + } } } - } - if let Some(pyproject) = settings_toml(&directory).ok().flatten() { - let Ok(settings) = ruff_workspace::resolver::resolve_root_settings( - &pyproject, - Relativity::Parent, - &EditorConfigurationTransformer(editor_settings, root), - ) else { - continue; - }; - index.insert( - directory, - Arc::new(RuffSettings { - path: Some(pyproject), - file_resolver: settings.file_resolver, - linter: settings.linter, - formatter: settings.formatter, - }), - ); - } - } + if let Some(pyproject) = settings_toml(&directory).ok().flatten() { + let Ok(settings) = ruff_workspace::resolver::resolve_root_settings( + &pyproject, + Relativity::Parent, + &EditorConfigurationTransformer(editor_settings, root), + ) else { + return WalkState::Continue; + }; + index.write().unwrap().insert( + directory, + Arc::new(RuffSettings { + path: Some(pyproject), + file_resolver: settings.file_resolver, + linter: settings.linter, + formatter: settings.formatter, + }), + ); + } + + WalkState::Continue + }) + }); let fallback = Arc::new(RuffSettings::fallback(editor_settings, root)); - Self { index, fallback } + Self { + index: index.into_inner().unwrap(), + fallback, + } } pub(super) fn get(&self, document_path: &Path) -> Arc {