Skip to content

Commit 34a5063

Browse files
Respect excludes in ruff server configuration discovery (#11551)
## Summary Right now, we're discovering configuration files even within (e.g.) virtual environments, because we're recursing without respecting the `exclude` field on parent configuration. Closes astral-sh/ruff-vscode#478. ## Test Plan Installed Pandas; verified that I saw no warnings: ![Screenshot 2024-05-26 at 8 09 05 PM](https://github.com/astral-sh/ruff/assets/1309177/dcf4115c-d7b3-453b-b7c7-afdd4804d6f5)
1 parent adc0a5d commit 34a5063

File tree

3 files changed

+60
-19
lines changed

3 files changed

+60
-19
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff_server/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ ruff_workspace = { workspace = true }
2828

2929
anyhow = { workspace = true }
3030
crossbeam = { workspace = true }
31+
globset = { workspace = true }
3132
jod-thread = { workspace = true }
3233
lsp-server = { workspace = true }
3334
lsp-types = { workspace = true }

crates/ruff_server/src/session/index/ruff_settings.rs

+58-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
use globset::Candidate;
12
use ruff_linter::{
23
display_settings, fs::normalize_path_to, settings::types::FilePattern,
34
settings::types::PreviewMode,
45
};
6+
use ruff_workspace::resolver::match_candidate_exclusion;
57
use ruff_workspace::{
68
configuration::{Configuration, FormatConfiguration, LintConfiguration, RuleSelection},
79
pyproject::{find_user_settings_toml, settings_toml},
@@ -12,12 +14,13 @@ use std::{
1214
path::{Path, PathBuf},
1315
sync::Arc,
1416
};
15-
use walkdir::{DirEntry, WalkDir};
17+
use walkdir::WalkDir;
1618

1719
use crate::session::settings::{ConfigurationPreference, ResolvedEditorSettings};
1820

19-
#[derive(Default)]
2021
pub(crate) struct RuffSettings {
22+
/// Settings used to manage file inclusion and exclusion.
23+
file_resolver: ruff_workspace::FileResolverSettings,
2124
/// Settings to pass into the Ruff linter.
2225
linter: ruff_linter::settings::LinterSettings,
2326
/// Settings to pass into the Ruff formatter.
@@ -54,7 +57,7 @@ impl RuffSettings {
5457
.ok()
5558
})
5659
.unwrap_or_else(|| {
57-
let default_configuration = ruff_workspace::configuration::Configuration::default();
60+
let default_configuration = Configuration::default();
5861
EditorConfigurationTransformer(editor_settings, root)
5962
.transform(default_configuration)
6063
.into_settings(root)
@@ -64,6 +67,7 @@ impl RuffSettings {
6467
});
6568

6669
RuffSettings {
70+
file_resolver: fallback.file_resolver,
6771
formatter: fallback.formatter,
6872
linter: fallback.linter,
6973
}
@@ -85,20 +89,18 @@ impl RuffSettingsIndex {
8589
// Add any settings from above the workspace root.
8690
for directory in root.ancestors() {
8791
if let Some(pyproject) = settings_toml(directory).ok().flatten() {
88-
if index.contains_key(&pyproject) {
89-
continue;
90-
}
91-
9292
let Ok(settings) = ruff_workspace::resolver::resolve_root_settings(
9393
&pyproject,
9494
Relativity::Parent,
9595
&EditorConfigurationTransformer(editor_settings, root),
9696
) else {
9797
continue;
9898
};
99+
99100
index.insert(
100101
directory.to_path_buf(),
101102
Arc::new(RuffSettings {
103+
file_resolver: settings.file_resolver,
102104
linter: settings.linter,
103105
formatter: settings.formatter,
104106
}),
@@ -107,18 +109,55 @@ impl RuffSettingsIndex {
107109
}
108110
}
109111

110-
// Add any settings within the workspace itself.
111-
for directory in WalkDir::new(root)
112-
.into_iter()
113-
.filter_map(Result::ok)
114-
.filter(|entry| entry.file_type().is_dir())
115-
.map(DirEntry::into_path)
116-
{
117-
if let Some(pyproject) = settings_toml(&directory).ok().flatten() {
118-
if index.contains_key(&pyproject) {
119-
continue;
112+
// Add any settings within the workspace itself
113+
let mut walker = WalkDir::new(root).into_iter();
114+
115+
while let Some(entry) = walker.next() {
116+
let Ok(entry) = entry else {
117+
continue;
118+
};
119+
120+
// Skip non-directories.
121+
if !entry.file_type().is_dir() {
122+
continue;
123+
}
124+
125+
let directory = entry.into_path();
126+
127+
// If the directory is excluded from the workspace, skip it.
128+
if let Some(file_name) = directory.file_name() {
129+
if let Some((_, settings)) = index
130+
.range(..directory.clone())
131+
.rfind(|(path, _)| directory.starts_with(path))
132+
{
133+
let candidate = Candidate::new(&directory);
134+
let basename = Candidate::new(file_name);
135+
if match_candidate_exclusion(
136+
&candidate,
137+
&basename,
138+
&settings.file_resolver.exclude,
139+
) {
140+
tracing::debug!("Ignored path via `exclude`: {}", directory.display());
141+
142+
walker.skip_current_dir();
143+
continue;
144+
} else if match_candidate_exclusion(
145+
&candidate,
146+
&basename,
147+
&settings.file_resolver.extend_exclude,
148+
) {
149+
tracing::debug!(
150+
"Ignored path via `extend-exclude`: {}",
151+
directory.display()
152+
);
153+
154+
walker.skip_current_dir();
155+
continue;
156+
}
120157
}
158+
}
121159

160+
if let Some(pyproject) = settings_toml(&directory).ok().flatten() {
122161
let Ok(settings) = ruff_workspace::resolver::resolve_root_settings(
123162
&pyproject,
124163
Relativity::Parent,
@@ -129,6 +168,7 @@ impl RuffSettingsIndex {
129168
index.insert(
130169
directory,
131170
Arc::new(RuffSettings {
171+
file_resolver: settings.file_resolver,
132172
linter: settings.linter,
133173
formatter: settings.formatter,
134174
}),
@@ -145,8 +185,7 @@ impl RuffSettingsIndex {
145185
if let Some((_, settings)) = self
146186
.index
147187
.range(..document_path.to_path_buf())
148-
.rev()
149-
.find(|(path, _)| document_path.starts_with(path))
188+
.rfind(|(path, _)| document_path.starts_with(path))
150189
{
151190
return settings.clone();
152191
}

0 commit comments

Comments
 (0)