mirror of https://github.com/astral-sh/ruff
feat: Add isort option lines-after-imports (#2440)
Fixes https://github.com/charliermarsh/ruff/issues/2243 Adds support for the isort option [lines_after_imports](https://pycqa.github.io/isort/docs/configuration/options.html#lines-after-imports) to insert blank lines between imports and the follow up code.
This commit is contained in:
parent
68422d4ff2
commit
ec7b25290b
19
README.md
19
README.md
|
|
@ -3500,6 +3500,25 @@ known-third-party = ["src"]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### [`lines-after-imports`](#lines-after-imports)
|
||||||
|
|
||||||
|
The number of blank lines to place after imports.
|
||||||
|
-1 for automatic determination.
|
||||||
|
|
||||||
|
**Default value**: `-1`
|
||||||
|
|
||||||
|
**Type**: `int`
|
||||||
|
|
||||||
|
**Example usage**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[tool.ruff.isort]
|
||||||
|
# Use a single line after each import block.
|
||||||
|
lines-after-imports = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
#### [`no-lines-before`](#no-lines-before)
|
#### [`no-lines-before`](#no-lines-before)
|
||||||
|
|
||||||
A list of sections that should _not_ be delineated from the previous
|
A list of sections that should _not_ be delineated from the previous
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from requests import Session
|
||||||
|
|
||||||
|
from my_first_party import my_first_party_object
|
||||||
|
|
||||||
|
from . import my_local_folder_object
|
||||||
|
class Thing(object):
|
||||||
|
name: str
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self.name = name
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from requests import Session
|
||||||
|
|
||||||
|
from my_first_party import my_first_party_object
|
||||||
|
|
||||||
|
from . import my_local_folder_object
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
my_local_folder_object.get()
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from requests import Session
|
||||||
|
|
||||||
|
from my_first_party import my_first_party_object
|
||||||
|
|
||||||
|
from . import my_local_folder_object
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 88
|
line-length = 88
|
||||||
|
|
||||||
|
[tool.ruff.isort]
|
||||||
|
lines-after-imports = 3
|
||||||
|
|
|
||||||
|
|
@ -957,6 +957,14 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lines-after-imports": {
|
||||||
|
"description": "The number of blank lines to place after imports. -1 for automatic determination.",
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"format": "int"
|
||||||
|
},
|
||||||
"no-lines-before": {
|
"no-lines-before": {
|
||||||
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
|
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
|
||||||
"type": [
|
"type": [
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use itertools::Either::{Left, Right};
|
||||||
|
|
||||||
use annotate::annotate_imports;
|
use annotate::annotate_imports;
|
||||||
use categorize::categorize_imports;
|
use categorize::categorize_imports;
|
||||||
pub use categorize::{categorize, ImportType};
|
pub use categorize::{categorize, ImportType};
|
||||||
use comments::Comment;
|
use comments::Comment;
|
||||||
use itertools::Either::{Left, Right};
|
|
||||||
use normalize::normalize_imports;
|
use normalize::normalize_imports;
|
||||||
use order::order_imports;
|
use order::order_imports;
|
||||||
use settings::RelativeImportsOrder;
|
use settings::RelativeImportsOrder;
|
||||||
|
|
@ -127,6 +128,7 @@ pub fn format_imports(
|
||||||
constants: &BTreeSet<String>,
|
constants: &BTreeSet<String>,
|
||||||
variables: &BTreeSet<String>,
|
variables: &BTreeSet<String>,
|
||||||
no_lines_before: &BTreeSet<ImportType>,
|
no_lines_before: &BTreeSet<ImportType>,
|
||||||
|
lines_after_imports: isize,
|
||||||
) -> String {
|
) -> String {
|
||||||
let trailer = &block.trailer;
|
let trailer = &block.trailer;
|
||||||
let block = annotate_imports(&block.imports, comments, locator, split_on_trailing_comma);
|
let block = annotate_imports(&block.imports, comments, locator, split_on_trailing_comma);
|
||||||
|
|
@ -214,11 +216,23 @@ pub fn format_imports(
|
||||||
match trailer {
|
match trailer {
|
||||||
None => {}
|
None => {}
|
||||||
Some(Trailer::Sibling) => {
|
Some(Trailer::Sibling) => {
|
||||||
output.push_str(stylist.line_ending());
|
if lines_after_imports >= 0 {
|
||||||
|
for _ in 0..lines_after_imports {
|
||||||
|
output.push_str(stylist.line_ending());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.push_str(stylist.line_ending());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(Trailer::FunctionDef | Trailer::ClassDef) => {
|
Some(Trailer::FunctionDef | Trailer::ClassDef) => {
|
||||||
output.push_str(stylist.line_ending());
|
if lines_after_imports >= 0 {
|
||||||
output.push_str(stylist.line_ending());
|
for _ in 0..lines_after_imports {
|
||||||
|
output.push_str(stylist.line_ending());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.push_str(stylist.line_ending());
|
||||||
|
output.push_str(stylist.line_ending());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output
|
output
|
||||||
|
|
@ -232,13 +246,14 @@ mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
use super::categorize::ImportType;
|
|
||||||
use super::settings::RelativeImportsOrder;
|
|
||||||
use crate::assert_yaml_snapshot;
|
use crate::assert_yaml_snapshot;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::test::{test_path, test_resource_path};
|
use crate::test::{test_path, test_resource_path};
|
||||||
|
|
||||||
|
use super::categorize::ImportType;
|
||||||
|
use super::settings::RelativeImportsOrder;
|
||||||
|
|
||||||
#[test_case(Path::new("add_newline_before_comments.py"))]
|
#[test_case(Path::new("add_newline_before_comments.py"))]
|
||||||
#[test_case(Path::new("combine_as_imports.py"))]
|
#[test_case(Path::new("combine_as_imports.py"))]
|
||||||
#[test_case(Path::new("combine_import_from.py"))]
|
#[test_case(Path::new("combine_import_from.py"))]
|
||||||
|
|
@ -641,4 +656,25 @@ mod tests {
|
||||||
assert_yaml_snapshot!(snapshot, diagnostics);
|
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(Path::new("lines_after_imports_nothing_after.py"))]
|
||||||
|
#[test_case(Path::new("lines_after_imports_func_after.py"))]
|
||||||
|
#[test_case(Path::new("lines_after_imports_class_after.py"))]
|
||||||
|
fn lines_after_imports(path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!("lines_after_imports_{}", path.to_string_lossy());
|
||||||
|
let mut diagnostics = test_path(
|
||||||
|
Path::new("isort").join(path).as_path(),
|
||||||
|
&Settings {
|
||||||
|
isort: super::settings::Settings {
|
||||||
|
lines_after_imports: 3,
|
||||||
|
..super::settings::Settings::default()
|
||||||
|
},
|
||||||
|
src: vec![test_resource_path("fixtures/isort")],
|
||||||
|
..Settings::for_rule(Rule::UnsortedImports)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
diagnostics.sort_by_key(|diagnostic| diagnostic.location);
|
||||||
|
assert_yaml_snapshot!(snapshot, diagnostics);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ fn matches_ignoring_indentation(val1: &str, val2: &str) -> bool {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
/// I001
|
/// I001
|
||||||
pub fn organize_imports(
|
pub fn organize_imports(
|
||||||
block: &Block,
|
block: &Block,
|
||||||
|
|
@ -117,6 +118,7 @@ pub fn organize_imports(
|
||||||
&settings.isort.constants,
|
&settings.isort.constants,
|
||||||
&settings.isort.variables,
|
&settings.isort.variables,
|
||||||
&settings.isort.no_lines_before,
|
&settings.isort.no_lines_before,
|
||||||
|
settings.isort.lines_after_imports,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Expand the span the entire range, including leading and trailing space.
|
// Expand the span the entire range, including leading and trailing space.
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,17 @@ pub struct Options {
|
||||||
/// A list of sections that should _not_ be delineated from the previous
|
/// A list of sections that should _not_ be delineated from the previous
|
||||||
/// section via empty lines.
|
/// section via empty lines.
|
||||||
pub no_lines_before: Option<Vec<ImportType>>,
|
pub no_lines_before: Option<Vec<ImportType>>,
|
||||||
|
#[option(
|
||||||
|
default = r#"-1"#,
|
||||||
|
value_type = "int",
|
||||||
|
example = r#"
|
||||||
|
# Use a single line after each import block.
|
||||||
|
lines-after-imports = 1
|
||||||
|
"#
|
||||||
|
)]
|
||||||
|
/// The number of blank lines to place after imports.
|
||||||
|
/// -1 for automatic determination.
|
||||||
|
pub lines_after_imports: Option<isize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
|
|
@ -234,6 +245,7 @@ pub struct Settings {
|
||||||
pub constants: BTreeSet<String>,
|
pub constants: BTreeSet<String>,
|
||||||
pub variables: BTreeSet<String>,
|
pub variables: BTreeSet<String>,
|
||||||
pub no_lines_before: BTreeSet<ImportType>,
|
pub no_lines_before: BTreeSet<ImportType>,
|
||||||
|
pub lines_after_imports: isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
|
|
@ -255,6 +267,7 @@ impl Default for Settings {
|
||||||
constants: BTreeSet::new(),
|
constants: BTreeSet::new(),
|
||||||
variables: BTreeSet::new(),
|
variables: BTreeSet::new(),
|
||||||
no_lines_before: BTreeSet::new(),
|
no_lines_before: BTreeSet::new(),
|
||||||
|
lines_after_imports: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -282,6 +295,7 @@ impl From<Options> for Settings {
|
||||||
constants: BTreeSet::from_iter(options.constants.unwrap_or_default()),
|
constants: BTreeSet::from_iter(options.constants.unwrap_or_default()),
|
||||||
variables: BTreeSet::from_iter(options.variables.unwrap_or_default()),
|
variables: BTreeSet::from_iter(options.variables.unwrap_or_default()),
|
||||||
no_lines_before: BTreeSet::from_iter(options.no_lines_before.unwrap_or_default()),
|
no_lines_before: BTreeSet::from_iter(options.no_lines_before.unwrap_or_default()),
|
||||||
|
lines_after_imports: options.lines_after_imports.unwrap_or(-1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -305,6 +319,7 @@ impl From<Settings> for Options {
|
||||||
constants: Some(settings.constants.into_iter().collect()),
|
constants: Some(settings.constants.into_iter().collect()),
|
||||||
variables: Some(settings.variables.into_iter().collect()),
|
variables: Some(settings.variables.into_iter().collect()),
|
||||||
no_lines_before: Some(settings.no_lines_before.into_iter().collect()),
|
no_lines_before: Some(settings.no_lines_before.into_iter().collect()),
|
||||||
|
lines_after_imports: Some(settings.lines_after_imports),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
source: src/rules/isort/mod.rs
|
||||||
|
expression: diagnostics
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnsortedImports: ~
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from __future__ import annotations
|
||||||
|
- ""
|
||||||
|
- from typing import Any
|
||||||
|
- ""
|
||||||
|
- from my_first_party import my_first_party_object
|
||||||
|
- from requests import Session
|
||||||
|
- ""
|
||||||
|
- from . import my_local_folder_object
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
source: src/rules/isort/mod.rs
|
||||||
|
expression: diagnostics
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnsortedImports: ~
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 21
|
||||||
|
column: 0
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from __future__ import annotations
|
||||||
|
- ""
|
||||||
|
- from typing import Any
|
||||||
|
- ""
|
||||||
|
- from my_first_party import my_first_party_object
|
||||||
|
- from requests import Session
|
||||||
|
- ""
|
||||||
|
- from . import my_local_folder_object
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
- ""
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 21
|
||||||
|
column: 0
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
source: src/rules/isort/mod.rs
|
||||||
|
expression: diagnostics
|
||||||
|
---
|
||||||
|
- kind:
|
||||||
|
UnsortedImports: ~
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from __future__ import annotations
|
||||||
|
- ""
|
||||||
|
- from typing import Any
|
||||||
|
- ""
|
||||||
|
- from my_first_party import my_first_party_object
|
||||||
|
- from requests import Session
|
||||||
|
- ""
|
||||||
|
- from . import my_local_folder_object
|
||||||
|
- ""
|
||||||
|
location:
|
||||||
|
row: 1
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
parent: ~
|
||||||
|
|
||||||
Loading…
Reference in New Issue