ruff server respects per-file-ignores configuration (#11224)

## Summary

Fixes #11185
Fixes #11214 

Document path and package information is now forwarded to the Ruff
linter, which allows `per-file-ignores` to correctly match against the
file name. This also fixes an issue where the import sorting rule didn't
distinguish between third-party and first-party packages since we didn't
pass in the package root.

## Test Plan

`per-file-ignores` should ignore files as expected. One quick way to
check is by adding this to your `pyproject.toml`:
```toml
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["ALL"]
```

Then, confirm that no diagnostics appear when you add code to an
`__init__.py` file (besides syntax errors).

The import sorting fix can be verified by failing to reproduce the
original issue - an `I001` diagnostic should not appear in
`other_module.py`.
This commit is contained in:
Jane Lewis 2024-05-01 19:24:35 -07:00 committed by GitHub
parent 653c8d83e9
commit 4aac1d1db9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 40 additions and 11 deletions

View file

@ -1,11 +1,12 @@
use ruff_linter::{ use ruff_linter::{
linter::{FixerResult, LinterResult}, linter::{FixerResult, LinterResult},
packaging::detect_package_root,
settings::{flags, types::UnsafeFixes, LinterSettings}, settings::{flags, types::UnsafeFixes, LinterSettings},
source_kind::SourceKind, source_kind::SourceKind,
}; };
use ruff_python_ast::PySourceType; use ruff_python_ast::PySourceType;
use ruff_source_file::LineIndex; use ruff_source_file::LineIndex;
use std::{borrow::Cow, path::Path}; use std::borrow::Cow;
use crate::{ use crate::{
edit::{Replacement, ToRangeExt}, edit::{Replacement, ToRangeExt},
@ -14,11 +15,23 @@ use crate::{
pub(crate) fn fix_all( pub(crate) fn fix_all(
document: &crate::edit::Document, document: &crate::edit::Document,
document_url: &lsp_types::Url,
linter_settings: &LinterSettings, linter_settings: &LinterSettings,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> crate::Result<Vec<lsp_types::TextEdit>> { ) -> crate::Result<Vec<lsp_types::TextEdit>> {
let source = document.contents(); let source = document.contents();
let document_path = document_url
.to_file_path()
.expect("document URL should be a valid file path");
let package = detect_package_root(
document_path
.parent()
.expect("a path to a document should have a parent path"),
&linter_settings.namespace_packages,
);
let source_type = PySourceType::default(); let source_type = PySourceType::default();
// TODO(jane): Support Jupyter Notebooks // TODO(jane): Support Jupyter Notebooks
@ -35,8 +48,8 @@ pub(crate) fn fix_all(
result: LinterResult { error, .. }, result: LinterResult { error, .. },
.. ..
} = ruff_linter::linter::lint_fix( } = ruff_linter::linter::lint_fix(
Path::new("<filename>"), &document_path,
None, package,
flags::Noqa::Enabled, flags::Noqa::Enabled,
UnsafeFixes::Disabled, UnsafeFixes::Disabled,
linter_settings, linter_settings,

View file

@ -1,11 +1,10 @@
//! Access to the Ruff linting API for the LSP //! Access to the Ruff linting API for the LSP
use std::path::Path;
use ruff_diagnostics::{Applicability, Diagnostic, DiagnosticKind, Fix}; use ruff_diagnostics::{Applicability, Diagnostic, DiagnosticKind, Fix};
use ruff_linter::{ use ruff_linter::{
directives::{extract_directives, Flags}, directives::{extract_directives, Flags},
linter::{check_path, LinterResult, TokenSource}, linter::{check_path, LinterResult, TokenSource},
packaging::detect_package_root,
registry::AsRule, registry::AsRule,
settings::{flags, LinterSettings}, settings::{flags, LinterSettings},
source_kind::SourceKind, source_kind::SourceKind,
@ -40,12 +39,24 @@ pub(crate) struct DiagnosticFix {
pub(crate) fn check( pub(crate) fn check(
document: &crate::edit::Document, document: &crate::edit::Document,
document_url: &lsp_types::Url,
linter_settings: &LinterSettings, linter_settings: &LinterSettings,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> Vec<lsp_types::Diagnostic> { ) -> Vec<lsp_types::Diagnostic> {
let contents = document.contents(); let contents = document.contents();
let index = document.index().clone(); let index = document.index().clone();
let document_path = document_url
.to_file_path()
.expect("document URL should be a valid file path");
let package = detect_package_root(
document_path
.parent()
.expect("a path to a document should have a parent path"),
&linter_settings.namespace_packages,
);
let source_type = PySourceType::default(); let source_type = PySourceType::default();
// TODO(jane): Support Jupyter Notebooks // TODO(jane): Support Jupyter Notebooks
@ -71,8 +82,8 @@ pub(crate) fn check(
data: (diagnostics, _imports), data: (diagnostics, _imports),
.. ..
} = check_path( } = check_path(
Path::new("<filename>"), &document_path,
None, package,
&locator, &locator,
&stylist, &stylist,
&indexer, &indexer,

View file

@ -6,6 +6,7 @@ pub(super) fn generate_diagnostics(snapshot: &DocumentSnapshot) -> Vec<lsp_types
if snapshot.client_settings().lint() { if snapshot.client_settings().lint() {
crate::lint::check( crate::lint::check(
snapshot.document(), snapshot.document(),
snapshot.url(),
snapshot.settings().linter(), snapshot.settings().linter(),
snapshot.encoding(), snapshot.encoding(),
) )

View file

@ -95,17 +95,18 @@ pub(super) fn resolve_edit_for_fix_all(
tracker.set_edits_for_document( tracker.set_edits_for_document(
url.clone(), url.clone(),
version, version,
fix_all_edit(document, linter_settings, encoding)?, fix_all_edit(document, url, linter_settings, encoding)?,
)?; )?;
Ok(tracker.into_workspace_edit()) Ok(tracker.into_workspace_edit())
} }
pub(super) fn fix_all_edit( pub(super) fn fix_all_edit(
document: &crate::edit::Document, document: &crate::edit::Document,
document_url: &types::Url,
linter_settings: &LinterSettings, linter_settings: &LinterSettings,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> crate::Result<Vec<types::TextEdit>> { ) -> crate::Result<Vec<types::TextEdit>> {
crate::fix::fix_all(document, linter_settings, encoding) crate::fix::fix_all(document, document_url, linter_settings, encoding)
} }
pub(super) fn resolve_edit_for_organize_imports( pub(super) fn resolve_edit_for_organize_imports(
@ -120,13 +121,14 @@ pub(super) fn resolve_edit_for_organize_imports(
tracker.set_edits_for_document( tracker.set_edits_for_document(
url.clone(), url.clone(),
version, version,
organize_imports_edit(document, linter_settings, encoding)?, organize_imports_edit(document, url, linter_settings, encoding)?,
)?; )?;
Ok(tracker.into_workspace_edit()) Ok(tracker.into_workspace_edit())
} }
pub(super) fn organize_imports_edit( pub(super) fn organize_imports_edit(
document: &crate::edit::Document, document: &crate::edit::Document,
document_url: &types::Url,
linter_settings: &LinterSettings, linter_settings: &LinterSettings,
encoding: PositionEncoding, encoding: PositionEncoding,
) -> crate::Result<Vec<types::TextEdit>> { ) -> crate::Result<Vec<types::TextEdit>> {
@ -138,5 +140,5 @@ pub(super) fn organize_imports_edit(
.into_iter() .into_iter()
.collect(); .collect();
crate::fix::fix_all(document, &linter_settings, encoding) crate::fix::fix_all(document, document_url, &linter_settings, encoding)
} }

View file

@ -64,6 +64,7 @@ impl super::SyncRequestHandler for ExecuteCommand {
Command::FixAll => { Command::FixAll => {
let edits = super::code_action_resolve::fix_all_edit( let edits = super::code_action_resolve::fix_all_edit(
snapshot.document(), snapshot.document(),
snapshot.url(),
snapshot.settings().linter(), snapshot.settings().linter(),
snapshot.encoding(), snapshot.encoding(),
) )
@ -83,6 +84,7 @@ impl super::SyncRequestHandler for ExecuteCommand {
Command::OrganizeImports => { Command::OrganizeImports => {
let edits = super::code_action_resolve::organize_imports_edit( let edits = super::code_action_resolve::organize_imports_edit(
snapshot.document(), snapshot.document(),
snapshot.url(),
snapshot.settings().linter(), snapshot.settings().linter(),
snapshot.encoding(), snapshot.encoding(),
) )