mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 14:52:01 +00:00
Add per-file-target-version
option (#16257)
## Summary
This PR is another step in preparing to detect syntax errors in the
parser. It introduces the new `per-file-target-version` top-level
configuration option, which holds a mapping of compiled glob patterns to
Python versions. I intend to use the
`LinterSettings::resolve_target_version` method here to pass to the
parser:
f50849aeef/crates/ruff_linter/src/linter.rs (L491-L493)
## Test Plan
I added two new CLI tests to show that the `per-file-target-version` is
respected in both the formatter and the linter.
This commit is contained in:
parent
42a5f5ef6a
commit
e7a6c19e3a
78 changed files with 820 additions and 274 deletions
|
@ -1,3 +1,5 @@
|
|||
use std::path::Path;
|
||||
|
||||
use ruff_formatter::PrintedRange;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_formatter::{format_module_source, FormatModuleError};
|
||||
|
@ -10,8 +12,10 @@ pub(crate) fn format(
|
|||
document: &TextDocument,
|
||||
source_type: PySourceType,
|
||||
formatter_settings: &FormatterSettings,
|
||||
path: Option<&Path>,
|
||||
) -> crate::Result<Option<String>> {
|
||||
let format_options = formatter_settings.to_format_options(source_type, document.contents());
|
||||
let format_options =
|
||||
formatter_settings.to_format_options(source_type, document.contents(), path);
|
||||
match format_module_source(document.contents(), format_options) {
|
||||
Ok(formatted) => {
|
||||
let formatted = formatted.into_code();
|
||||
|
@ -36,8 +40,10 @@ pub(crate) fn format_range(
|
|||
source_type: PySourceType,
|
||||
formatter_settings: &FormatterSettings,
|
||||
range: TextRange,
|
||||
path: Option<&Path>,
|
||||
) -> crate::Result<Option<PrintedRange>> {
|
||||
let format_options = formatter_settings.to_format_options(source_type, document.contents());
|
||||
let format_options =
|
||||
formatter_settings.to_format_options(source_type, document.contents(), path);
|
||||
|
||||
match ruff_python_formatter::format_range(document.contents(), range, format_options) {
|
||||
Ok(formatted) => {
|
||||
|
@ -56,3 +62,146 @@ pub(crate) fn format_range(
|
|||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use insta::assert_snapshot;
|
||||
use ruff_linter::settings::types::{CompiledPerFileTargetVersionList, PerFileTargetVersion};
|
||||
use ruff_python_ast::{PySourceType, PythonVersion};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_workspace::FormatterSettings;
|
||||
|
||||
use crate::format::{format, format_range};
|
||||
use crate::TextDocument;
|
||||
|
||||
#[test]
|
||||
fn format_per_file_version() {
|
||||
let document = TextDocument::new(r#"
|
||||
with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open("a_really_long_baz") as baz:
|
||||
pass
|
||||
"#.to_string(), 0);
|
||||
let per_file_target_version =
|
||||
CompiledPerFileTargetVersionList::resolve(vec![PerFileTargetVersion::new(
|
||||
"test.py".to_string(),
|
||||
PythonVersion::PY310,
|
||||
Some(Path::new(".")),
|
||||
)])
|
||||
.unwrap();
|
||||
let result = format(
|
||||
&document,
|
||||
PySourceType::Python,
|
||||
&FormatterSettings {
|
||||
unresolved_target_version: PythonVersion::PY38,
|
||||
per_file_target_version,
|
||||
..Default::default()
|
||||
},
|
||||
Some(Path::new("test.py")),
|
||||
)
|
||||
.expect("Expected no errors when formatting")
|
||||
.expect("Expected formatting changes");
|
||||
|
||||
assert_snapshot!(result, @r#"
|
||||
with (
|
||||
open("a_really_long_foo") as foo,
|
||||
open("a_really_long_bar") as bar,
|
||||
open("a_really_long_baz") as baz,
|
||||
):
|
||||
pass
|
||||
"#);
|
||||
|
||||
// same as above but without the per_file_target_version override
|
||||
let result = format(
|
||||
&document,
|
||||
PySourceType::Python,
|
||||
&FormatterSettings {
|
||||
unresolved_target_version: PythonVersion::PY38,
|
||||
..Default::default()
|
||||
},
|
||||
Some(Path::new("test.py")),
|
||||
)
|
||||
.expect("Expected no errors when formatting")
|
||||
.expect("Expected formatting changes");
|
||||
|
||||
assert_snapshot!(result, @r#"
|
||||
with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open(
|
||||
"a_really_long_baz"
|
||||
) as baz:
|
||||
pass
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_per_file_version_range() -> anyhow::Result<()> {
|
||||
// prepare a document with formatting changes before and after the intended range (the
|
||||
// context manager)
|
||||
let document = TextDocument::new(r#"
|
||||
def fn(x: str) -> Foo | Bar: return foobar(x)
|
||||
|
||||
with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open("a_really_long_baz") as baz:
|
||||
pass
|
||||
|
||||
sys.exit(
|
||||
1
|
||||
)
|
||||
"#.to_string(), 0);
|
||||
|
||||
let start = document.contents().find("with").unwrap();
|
||||
let end = document.contents().find("pass").unwrap() + "pass".len();
|
||||
let range = TextRange::new(TextSize::try_from(start)?, TextSize::try_from(end)?);
|
||||
|
||||
let per_file_target_version =
|
||||
CompiledPerFileTargetVersionList::resolve(vec![PerFileTargetVersion::new(
|
||||
"test.py".to_string(),
|
||||
PythonVersion::PY310,
|
||||
Some(Path::new(".")),
|
||||
)])
|
||||
.unwrap();
|
||||
let result = format_range(
|
||||
&document,
|
||||
PySourceType::Python,
|
||||
&FormatterSettings {
|
||||
unresolved_target_version: PythonVersion::PY38,
|
||||
per_file_target_version,
|
||||
..Default::default()
|
||||
},
|
||||
range,
|
||||
Some(Path::new("test.py")),
|
||||
)
|
||||
.expect("Expected no errors when formatting")
|
||||
.expect("Expected formatting changes");
|
||||
|
||||
assert_snapshot!(result.as_code(), @r#"
|
||||
with (
|
||||
open("a_really_long_foo") as foo,
|
||||
open("a_really_long_bar") as bar,
|
||||
open("a_really_long_baz") as baz,
|
||||
):
|
||||
pass
|
||||
"#);
|
||||
|
||||
// same as above but without the per_file_target_version override
|
||||
let result = format_range(
|
||||
&document,
|
||||
PySourceType::Python,
|
||||
&FormatterSettings {
|
||||
unresolved_target_version: PythonVersion::PY38,
|
||||
..Default::default()
|
||||
},
|
||||
range,
|
||||
Some(Path::new("test.py")),
|
||||
)
|
||||
.expect("Expected no errors when formatting")
|
||||
.expect("Expected formatting changes");
|
||||
|
||||
assert_snapshot!(result.as_code(), @r#"
|
||||
with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open(
|
||||
"a_really_long_baz"
|
||||
) as baz:
|
||||
pass
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,9 +85,10 @@ fn format_text_document(
|
|||
let settings = query.settings();
|
||||
|
||||
// If the document is excluded, return early.
|
||||
if let Some(file_path) = query.file_path() {
|
||||
let file_path = query.file_path();
|
||||
if let Some(file_path) = &file_path {
|
||||
if is_document_excluded_for_formatting(
|
||||
&file_path,
|
||||
file_path,
|
||||
&settings.file_resolver,
|
||||
&settings.formatter,
|
||||
text_document.language_id(),
|
||||
|
@ -97,8 +98,13 @@ fn format_text_document(
|
|||
}
|
||||
|
||||
let source = text_document.contents();
|
||||
let formatted = crate::format::format(text_document, query.source_type(), &settings.formatter)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
let formatted = crate::format::format(
|
||||
text_document,
|
||||
query.source_type(),
|
||||
&settings.formatter,
|
||||
file_path.as_deref(),
|
||||
)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
let Some(mut formatted) = formatted else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
|
|
@ -49,9 +49,10 @@ fn format_text_document_range(
|
|||
let settings = query.settings();
|
||||
|
||||
// If the document is excluded, return early.
|
||||
if let Some(file_path) = query.file_path() {
|
||||
let file_path = query.file_path();
|
||||
if let Some(file_path) = &file_path {
|
||||
if is_document_excluded_for_formatting(
|
||||
&file_path,
|
||||
file_path,
|
||||
&settings.file_resolver,
|
||||
&settings.formatter,
|
||||
text_document.language_id(),
|
||||
|
@ -68,6 +69,7 @@ fn format_text_document_range(
|
|||
query.source_type(),
|
||||
&settings.formatter,
|
||||
range,
|
||||
file_path.as_deref(),
|
||||
)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue