mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 04:45:01 +00:00

## 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`.
92 lines
2.7 KiB
Rust
92 lines
2.7 KiB
Rust
use ruff_linter::{
|
|
linter::{FixerResult, LinterResult},
|
|
packaging::detect_package_root,
|
|
settings::{flags, types::UnsafeFixes, LinterSettings},
|
|
source_kind::SourceKind,
|
|
};
|
|
use ruff_python_ast::PySourceType;
|
|
use ruff_source_file::LineIndex;
|
|
use std::borrow::Cow;
|
|
|
|
use crate::{
|
|
edit::{Replacement, ToRangeExt},
|
|
PositionEncoding,
|
|
};
|
|
|
|
pub(crate) fn fix_all(
|
|
document: &crate::edit::Document,
|
|
document_url: &lsp_types::Url,
|
|
linter_settings: &LinterSettings,
|
|
encoding: PositionEncoding,
|
|
) -> crate::Result<Vec<lsp_types::TextEdit>> {
|
|
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();
|
|
|
|
// TODO(jane): Support Jupyter Notebooks
|
|
let source_kind = SourceKind::Python(source.to_string());
|
|
|
|
// We need to iteratively apply all safe fixes onto a single file and then
|
|
// create a diff between the modified file and the original source to use as a single workspace
|
|
// edit.
|
|
// If we simply generated the diagnostics with `check_path` and then applied fixes individually,
|
|
// there's a possibility they could overlap or introduce new problems that need to be fixed,
|
|
// which is inconsistent with how `ruff check --fix` works.
|
|
let FixerResult {
|
|
transformed,
|
|
result: LinterResult { error, .. },
|
|
..
|
|
} = ruff_linter::linter::lint_fix(
|
|
&document_path,
|
|
package,
|
|
flags::Noqa::Enabled,
|
|
UnsafeFixes::Disabled,
|
|
linter_settings,
|
|
&source_kind,
|
|
source_type,
|
|
)?;
|
|
|
|
if let Some(error) = error {
|
|
// abort early if a parsing error occurred
|
|
return Err(anyhow::anyhow!(
|
|
"A parsing error occurred during `fix_all`: {error}"
|
|
));
|
|
}
|
|
|
|
// fast path: if `transformed` is still borrowed, no changes were made and we can return early
|
|
if let Cow::Borrowed(_) = transformed {
|
|
return Ok(vec![]);
|
|
}
|
|
|
|
let modified = transformed.source_code();
|
|
|
|
let modified_index = LineIndex::from_source_text(modified);
|
|
|
|
let source_index = document.index();
|
|
|
|
let Replacement {
|
|
source_range,
|
|
modified_range,
|
|
} = Replacement::between(
|
|
source,
|
|
source_index.line_starts(),
|
|
modified,
|
|
modified_index.line_starts(),
|
|
);
|
|
|
|
Ok(vec![lsp_types::TextEdit {
|
|
range: source_range.to_range(source, source_index, encoding),
|
|
new_text: modified[modified_range].to_owned(),
|
|
}])
|
|
}
|