mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:10:09 +00:00
Infer indentation with imports when logical indent is absent (#11608)
## Summary In an `__init__.py` file, it's not uncommon to lack a logical indent (since it may just contain imports). In such cases, we were always falling back to four-space indent. This PR adds detection for indents within import groups. Closes https://github.com/astral-sh/ruff/issues/11606.
This commit is contained in:
parent
a8d1328c1a
commit
bd46cd1fcf
6 changed files with 571 additions and 498 deletions
10
crates/ruff_linter/resources/test/fixtures/isort/two_space.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/isort/two_space.py
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# If the file doesn't contain a logical indent token, we should still detect two-space indentation on imports.
|
||||||
|
from math import (
|
||||||
|
sin,
|
||||||
|
tan,
|
||||||
|
cos,
|
||||||
|
nan,
|
||||||
|
pi,
|
||||||
|
)
|
||||||
|
|
||||||
|
del sin, cos, tan, pi, nan
|
|
@ -29,6 +29,11 @@ print("%#o" % (123,))
|
||||||
|
|
||||||
print("brace {} %s" % (1,))
|
print("brace {} %s" % (1,))
|
||||||
|
|
||||||
|
print((
|
||||||
|
"foo %s "
|
||||||
|
"bar %s" % (x, y)
|
||||||
|
))
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"%s" % (
|
"%s" % (
|
||||||
"trailing comma",
|
"trailing comma",
|
||||||
|
@ -52,10 +57,6 @@ print("%(ab)s" % {"a" "b": 1})
|
||||||
|
|
||||||
print("%(a)s" % {"a" : 1})
|
print("%(a)s" % {"a" : 1})
|
||||||
|
|
||||||
print((
|
|
||||||
"foo %s "
|
|
||||||
"bar %s" % (x, y)
|
|
||||||
))
|
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"foo %(foo)s "
|
"foo %(foo)s "
|
||||||
|
|
|
@ -341,6 +341,7 @@ mod tests {
|
||||||
#[test_case(Path::new("split.py"))]
|
#[test_case(Path::new("split.py"))]
|
||||||
#[test_case(Path::new("star_before_others.py"))]
|
#[test_case(Path::new("star_before_others.py"))]
|
||||||
#[test_case(Path::new("trailing_suffix.py"))]
|
#[test_case(Path::new("trailing_suffix.py"))]
|
||||||
|
#[test_case(Path::new("two_space.py"))]
|
||||||
#[test_case(Path::new("type_comments.py"))]
|
#[test_case(Path::new("type_comments.py"))]
|
||||||
#[test_case(Path::new("unicode.py"))]
|
#[test_case(Path::new("unicode.py"))]
|
||||||
fn default(path: &Path) -> Result<()> {
|
fn default(path: &Path) -> Result<()> {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||||
|
---
|
||||||
|
two_space.py:2:1: I001 [*] Import block is un-sorted or un-formatted
|
||||||
|
|
|
||||||
|
1 | # If the file doesn't contain a logical indent token, we should still detect two-space indentation on imports.
|
||||||
|
2 | / from math import (
|
||||||
|
3 | | sin,
|
||||||
|
4 | | tan,
|
||||||
|
5 | | cos,
|
||||||
|
6 | | nan,
|
||||||
|
7 | | pi,
|
||||||
|
8 | | )
|
||||||
|
9 | |
|
||||||
|
10 | | del sin, cos, tan, pi, nan
|
||||||
|
| |_^ I001
|
||||||
|
|
|
||||||
|
= help: Organize imports
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
1 1 | # If the file doesn't contain a logical indent token, we should still detect two-space indentation on imports.
|
||||||
|
2 2 | from math import (
|
||||||
|
3 |- sin,
|
||||||
|
4 |- tan,
|
||||||
|
5 3 | cos,
|
||||||
|
6 4 | nan,
|
||||||
|
7 5 | pi,
|
||||||
|
6 |+ sin,
|
||||||
|
7 |+ tan,
|
||||||
|
8 8 | )
|
||||||
|
9 9 |
|
||||||
|
10 10 | del sin, cos, tan, pi, nan
|
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@ use once_cell::unsync::OnceCell;
|
||||||
|
|
||||||
use ruff_python_ast::{str::Quote, StringFlags};
|
use ruff_python_ast::{str::Quote, StringFlags};
|
||||||
use ruff_python_parser::lexer::LexResult;
|
use ruff_python_parser::lexer::LexResult;
|
||||||
use ruff_python_parser::Tok;
|
use ruff_python_parser::{Tok, TokenKind};
|
||||||
use ruff_source_file::{find_newline, LineEnding, Locator};
|
use ruff_source_file::{find_newline, LineEnding, Locator};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -86,6 +86,38 @@ fn detect_indention(tokens: &[LexResult], locator: &Locator) -> Indentation {
|
||||||
|
|
||||||
Indentation(whitespace.to_string())
|
Indentation(whitespace.to_string())
|
||||||
} else {
|
} else {
|
||||||
|
// If we can't find a logical indent token, search for a non-logical indent, to cover cases
|
||||||
|
// like:
|
||||||
|
//```python
|
||||||
|
// from math import (
|
||||||
|
// sin,
|
||||||
|
// tan,
|
||||||
|
// cos,
|
||||||
|
// )
|
||||||
|
// ```
|
||||||
|
let mut depth = 0usize;
|
||||||
|
for (token, range) in tokens.iter().flatten() {
|
||||||
|
match token.kind() {
|
||||||
|
TokenKind::Lpar | TokenKind::Lbrace | TokenKind::Lsqb => {
|
||||||
|
depth = depth.saturating_add(1);
|
||||||
|
}
|
||||||
|
TokenKind::Rpar | TokenKind::Rbrace | TokenKind::Rsqb => {
|
||||||
|
depth = depth.saturating_sub(1);
|
||||||
|
}
|
||||||
|
TokenKind::NonLogicalNewline => {
|
||||||
|
let line = locator.line(range.end());
|
||||||
|
let indent_index = line.chars().position(|c| !c.is_whitespace());
|
||||||
|
if let Some(indent_index) = indent_index {
|
||||||
|
if indent_index > 0 {
|
||||||
|
let whitespace = &line[..indent_index];
|
||||||
|
return Indentation(whitespace.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Indentation::default()
|
Indentation::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +209,6 @@ if True:
|
||||||
&Indentation("\t".to_string())
|
&Indentation("\t".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(charlie): Should non-significant whitespace be detected?
|
|
||||||
let contents = r"
|
let contents = r"
|
||||||
x = (
|
x = (
|
||||||
1,
|
1,
|
||||||
|
@ -189,7 +220,7 @@ x = (
|
||||||
let tokens: Vec<_> = lex(contents, Mode::Module).collect();
|
let tokens: Vec<_> = lex(contents, Mode::Module).collect();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Stylist::from_tokens(&tokens, &locator).indentation(),
|
Stylist::from_tokens(&tokens, &locator).indentation(),
|
||||||
&Indentation::default()
|
&Indentation(" ".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
// formfeed indent, see `detect_indention` comment.
|
// formfeed indent, see `detect_indention` comment.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue