mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:21 +00:00
Conside include
, extend-include
for the native server (#12252)
## Summary
This PR updates the native server to consider the `include` and
`extend-include` file resolver settings.
fixes: #12242
## Test Plan
Note: Settings reloading doesn't work for nested configs which is fixed
in #12253 so the preview here only showcases root level config.
e8969128
-c175-4f98-8114-0d692b906cc8
This commit is contained in:
parent
855d62cdde
commit
0bb2fc6eec
10 changed files with 101 additions and 47 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2448,7 +2448,6 @@ version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"globset",
|
|
||||||
"insta",
|
"insta",
|
||||||
"jod-thread",
|
"jod-thread",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
|
@ -28,7 +28,6 @@ ruff_workspace = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
crossbeam = { workspace = true }
|
crossbeam = { workspace = true }
|
||||||
globset = { workspace = true }
|
|
||||||
jod-thread = { workspace = true }
|
jod-thread = { workspace = true }
|
||||||
lsp-server = { workspace = true }
|
lsp-server = { workspace = true }
|
||||||
lsp-types = { workspace = true }
|
lsp-types = { workspace = true }
|
||||||
|
|
|
@ -9,10 +9,10 @@ use ruff_linter::{
|
||||||
};
|
};
|
||||||
use ruff_notebook::SourceValue;
|
use ruff_notebook::SourceValue;
|
||||||
use ruff_source_file::LineIndex;
|
use ruff_source_file::LineIndex;
|
||||||
use ruff_workspace::resolver::match_any_exclusion;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
edit::{Replacement, ToRangeExt},
|
edit::{Replacement, ToRangeExt},
|
||||||
|
resolve::is_document_excluded,
|
||||||
session::DocumentQuery,
|
session::DocumentQuery,
|
||||||
PositionEncoding,
|
PositionEncoding,
|
||||||
};
|
};
|
||||||
|
@ -33,18 +33,12 @@ pub(crate) fn fix_all(
|
||||||
|
|
||||||
// If the document is excluded, return an empty list of fixes.
|
// If the document is excluded, return an empty list of fixes.
|
||||||
let package = if let Some(document_path) = document_path.as_ref() {
|
let package = if let Some(document_path) = document_path.as_ref() {
|
||||||
if let Some(exclusion) = match_any_exclusion(
|
if is_document_excluded(
|
||||||
document_path,
|
document_path,
|
||||||
&file_resolver_settings.exclude,
|
file_resolver_settings,
|
||||||
&file_resolver_settings.extend_exclude,
|
Some(linter_settings),
|
||||||
Some(&linter_settings.exclude),
|
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
tracing::debug!(
|
|
||||||
"Ignored path via `{}`: {}",
|
|
||||||
exclusion,
|
|
||||||
document_path.display()
|
|
||||||
);
|
|
||||||
return Ok(Fixes::default());
|
return Ok(Fixes::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod edit;
|
||||||
mod fix;
|
mod fix;
|
||||||
mod format;
|
mod format;
|
||||||
mod lint;
|
mod lint;
|
||||||
|
mod resolve;
|
||||||
mod server;
|
mod server;
|
||||||
mod session;
|
mod session;
|
||||||
mod trace;
|
mod trace;
|
||||||
|
|
|
@ -19,10 +19,10 @@ use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_source_file::{LineIndex, Locator};
|
use ruff_source_file::{LineIndex, Locator};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
use ruff_workspace::resolver::match_any_exclusion;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
edit::{NotebookRange, ToRangeExt},
|
edit::{NotebookRange, ToRangeExt},
|
||||||
|
resolve::is_document_excluded,
|
||||||
session::DocumentQuery,
|
session::DocumentQuery,
|
||||||
PositionEncoding, DIAGNOSTIC_NAME,
|
PositionEncoding, DIAGNOSTIC_NAME,
|
||||||
};
|
};
|
||||||
|
@ -72,18 +72,12 @@ pub(crate) fn check(
|
||||||
|
|
||||||
// If the document is excluded, return an empty list of diagnostics.
|
// If the document is excluded, return an empty list of diagnostics.
|
||||||
let package = if let Some(document_path) = document_path.as_ref() {
|
let package = if let Some(document_path) = document_path.as_ref() {
|
||||||
if let Some(exclusion) = match_any_exclusion(
|
if is_document_excluded(
|
||||||
document_path,
|
document_path,
|
||||||
&file_resolver_settings.exclude,
|
file_resolver_settings,
|
||||||
&file_resolver_settings.extend_exclude,
|
Some(linter_settings),
|
||||||
Some(&linter_settings.exclude),
|
|
||||||
None,
|
None,
|
||||||
) {
|
) {
|
||||||
tracing::debug!(
|
|
||||||
"Ignored path via `{}`: {}",
|
|
||||||
exclusion,
|
|
||||||
document_path.display()
|
|
||||||
);
|
|
||||||
return DiagnosticsMap::default();
|
return DiagnosticsMap::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
crates/ruff_server/src/resolve.rs
Normal file
45
crates/ruff_server/src/resolve.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use ruff_linter::settings::LinterSettings;
|
||||||
|
use ruff_workspace::resolver::{match_any_exclusion, match_any_inclusion};
|
||||||
|
use ruff_workspace::{FileResolverSettings, FormatterSettings};
|
||||||
|
|
||||||
|
/// Return `true` if the document at the given [`Path`] should be excluded.
|
||||||
|
///
|
||||||
|
/// The tool-specific settings should be provided if the request for the document is specific to
|
||||||
|
/// that tool. For example, a diagnostics request should provide the linter settings while the
|
||||||
|
/// formatting request should provide the formatter settings.
|
||||||
|
///
|
||||||
|
/// The logic for the resolution considers both inclusion and exclusion and is as follows:
|
||||||
|
/// 1. Check for global `exclude` and `extend-exclude` options along with tool specific `exclude`
|
||||||
|
/// option (`lint.exclude`, `format.exclude`).
|
||||||
|
/// 2. Check for global `include` and `extend-include` options.
|
||||||
|
pub(crate) fn is_document_excluded(
|
||||||
|
path: &Path,
|
||||||
|
resolver_settings: &FileResolverSettings,
|
||||||
|
linter_settings: Option<&LinterSettings>,
|
||||||
|
formatter_settings: Option<&FormatterSettings>,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(exclusion) = match_any_exclusion(
|
||||||
|
path,
|
||||||
|
&resolver_settings.exclude,
|
||||||
|
&resolver_settings.extend_exclude,
|
||||||
|
linter_settings.map(|s| &*s.exclude),
|
||||||
|
formatter_settings.map(|s| &*s.exclude),
|
||||||
|
) {
|
||||||
|
tracing::debug!("Ignored path via `{}`: {}", exclusion, path.display());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(inclusion) = match_any_inclusion(
|
||||||
|
path,
|
||||||
|
&resolver_settings.include,
|
||||||
|
&resolver_settings.extend_include,
|
||||||
|
) {
|
||||||
|
tracing::debug!("Included path via `{}`: {}", inclusion, path.display());
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// Path is excluded by not being in the inclusion set.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,10 @@ use lsp_types::{self as types, request as req};
|
||||||
use types::TextEdit;
|
use types::TextEdit;
|
||||||
|
|
||||||
use ruff_source_file::LineIndex;
|
use ruff_source_file::LineIndex;
|
||||||
use ruff_workspace::resolver::match_any_exclusion;
|
|
||||||
|
|
||||||
use crate::edit::{Replacement, ToRangeExt};
|
use crate::edit::{Replacement, ToRangeExt};
|
||||||
use crate::fix::Fixes;
|
use crate::fix::Fixes;
|
||||||
|
use crate::resolve::is_document_excluded;
|
||||||
use crate::server::api::LSPResult;
|
use crate::server::api::LSPResult;
|
||||||
use crate::server::{client::Notifier, Result};
|
use crate::server::{client::Notifier, Result};
|
||||||
use crate::session::{DocumentQuery, DocumentSnapshot};
|
use crate::session::{DocumentQuery, DocumentSnapshot};
|
||||||
|
@ -85,14 +85,12 @@ fn format_text_document(
|
||||||
|
|
||||||
// If the document is excluded, return early.
|
// If the document is excluded, return early.
|
||||||
if let Some(file_path) = query.file_path() {
|
if let Some(file_path) = query.file_path() {
|
||||||
if let Some(exclusion) = match_any_exclusion(
|
if is_document_excluded(
|
||||||
&file_path,
|
&file_path,
|
||||||
&file_resolver_settings.exclude,
|
file_resolver_settings,
|
||||||
&file_resolver_settings.extend_exclude,
|
|
||||||
None,
|
None,
|
||||||
Some(&formatter_settings.exclude),
|
Some(formatter_settings),
|
||||||
) {
|
) {
|
||||||
tracing::debug!("Ignored path via `{}`: {}", exclusion, file_path.display());
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use lsp_types::{self as types, request as req, Range};
|
use lsp_types::{self as types, request as req, Range};
|
||||||
|
|
||||||
use ruff_workspace::resolver::match_any_exclusion;
|
|
||||||
|
|
||||||
use crate::edit::{RangeExt, ToRangeExt};
|
use crate::edit::{RangeExt, ToRangeExt};
|
||||||
|
use crate::resolve::is_document_excluded;
|
||||||
use crate::server::api::LSPResult;
|
use crate::server::api::LSPResult;
|
||||||
use crate::server::{client::Notifier, Result};
|
use crate::server::{client::Notifier, Result};
|
||||||
use crate::session::{DocumentQuery, DocumentSnapshot};
|
use crate::session::{DocumentQuery, DocumentSnapshot};
|
||||||
|
@ -50,14 +49,12 @@ fn format_text_document_range(
|
||||||
|
|
||||||
// If the document is excluded, return early.
|
// If the document is excluded, return early.
|
||||||
if let Some(file_path) = query.file_path() {
|
if let Some(file_path) = query.file_path() {
|
||||||
if let Some(exclusion) = match_any_exclusion(
|
if is_document_excluded(
|
||||||
&file_path,
|
&file_path,
|
||||||
&file_resolver_settings.exclude,
|
file_resolver_settings,
|
||||||
&file_resolver_settings.extend_exclude,
|
|
||||||
None,
|
None,
|
||||||
Some(&formatter_settings.exclude),
|
Some(formatter_settings),
|
||||||
) {
|
) {
|
||||||
tracing::debug!("Ignored path via `{}`: {}", exclusion, file_path.display());
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use globset::Candidate;
|
|
||||||
use ruff_linter::{
|
use ruff_linter::{
|
||||||
display_settings, fs::normalize_path_to, settings::types::FilePattern,
|
display_settings, fs::normalize_path_to, settings::types::FilePattern,
|
||||||
settings::types::PreviewMode,
|
settings::types::PreviewMode,
|
||||||
};
|
};
|
||||||
use ruff_workspace::resolver::match_candidate_exclusion;
|
use ruff_workspace::resolver::match_exclusion;
|
||||||
use ruff_workspace::{
|
use ruff_workspace::{
|
||||||
configuration::{Configuration, FormatConfiguration, LintConfiguration, RuleSelection},
|
configuration::{Configuration, FormatConfiguration, LintConfiguration, RuleSelection},
|
||||||
pyproject::{find_user_settings_toml, settings_toml},
|
pyproject::{find_user_settings_toml, settings_toml},
|
||||||
|
@ -41,6 +40,7 @@ impl std::fmt::Display for RuffSettings {
|
||||||
display_settings! {
|
display_settings! {
|
||||||
formatter = f,
|
formatter = f,
|
||||||
fields = [
|
fields = [
|
||||||
|
self.file_resolver,
|
||||||
self.linter,
|
self.linter,
|
||||||
self.formatter
|
self.formatter
|
||||||
]
|
]
|
||||||
|
@ -146,20 +146,14 @@ impl RuffSettingsIndex {
|
||||||
.range(..directory.clone())
|
.range(..directory.clone())
|
||||||
.rfind(|(path, _)| directory.starts_with(path))
|
.rfind(|(path, _)| directory.starts_with(path))
|
||||||
{
|
{
|
||||||
let candidate = Candidate::new(&directory);
|
if match_exclusion(&directory, file_name, &settings.file_resolver.exclude) {
|
||||||
let basename = Candidate::new(file_name);
|
|
||||||
if match_candidate_exclusion(
|
|
||||||
&candidate,
|
|
||||||
&basename,
|
|
||||||
&settings.file_resolver.exclude,
|
|
||||||
) {
|
|
||||||
tracing::debug!("Ignored path via `exclude`: {}", directory.display());
|
tracing::debug!("Ignored path via `exclude`: {}", directory.display());
|
||||||
|
|
||||||
walker.skip_current_dir();
|
walker.skip_current_dir();
|
||||||
continue;
|
continue;
|
||||||
} else if match_candidate_exclusion(
|
} else if match_exclusion(
|
||||||
&candidate,
|
&directory,
|
||||||
&basename,
|
file_name,
|
||||||
&settings.file_resolver.extend_exclude,
|
&settings.file_resolver.extend_exclude,
|
||||||
) {
|
) {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
|
|
|
@ -683,6 +683,39 @@ pub fn match_any_exclusion(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum InclusionKind {
|
||||||
|
/// The inclusion came from the `include` setting.
|
||||||
|
Include,
|
||||||
|
/// The inclusion came from the `extend-include` setting.
|
||||||
|
ExtendInclude,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for InclusionKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
InclusionKind::Include => write!(f, "include"),
|
||||||
|
InclusionKind::ExtendInclude => write!(f, "extend-include"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the [`InclusionKind`] for a given [`Path`], if the path match any of the inclusion
|
||||||
|
/// criteria.
|
||||||
|
pub fn match_any_inclusion(
|
||||||
|
path: &Path,
|
||||||
|
include: &GlobSet,
|
||||||
|
extend_include: &GlobSet,
|
||||||
|
) -> Option<InclusionKind> {
|
||||||
|
if include.is_match(path) {
|
||||||
|
Some(InclusionKind::Include)
|
||||||
|
} else if extend_include.is_match(path) {
|
||||||
|
Some(InclusionKind::ExtendInclude)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::fs::{create_dir, File};
|
use std::fs::{create_dir, File};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue