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:
Dhruv Manilawala 2024-07-10 09:42:57 +05:30 committed by GitHub
parent 855d62cdde
commit 0bb2fc6eec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 101 additions and 47 deletions

1
Cargo.lock generated
View file

@ -2448,7 +2448,6 @@ version = "0.2.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"crossbeam", "crossbeam",
"globset",
"insta", "insta",
"jod-thread", "jod-thread",
"libc", "libc",

View file

@ -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 }

View file

@ -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());
} }

View file

@ -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;

View file

@ -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();
} }

View 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
}
}

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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!(

View file

@ -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};