diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/import.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/import.py index 1677400431..db65907847 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/import.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/import.py @@ -1,3 +1,58 @@ from a import aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa from a import aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa, aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa from a import aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa as dfgsdfgsd, aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa as sdkjflsdjlahlfd + +# At the top-level, force one empty line after an import, but allow up to two empty +# lines. +import os +import sys +x = 1 + +import os +import sys + +x = 1 + +import os +import sys + + +x = 1 + +import os +import sys + + + +x = 1 + + +# In a nested scope, force one empty line after an import. +def func(): + import os + import sys + x = 1 + + +def func(): + import os + import sys + + x = 1 + + +def func(): + import os + import sys + + + x = 1 + + +def func(): + import os + import sys + + + + x = 1 diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index e9b21176fe..24dca3ffc2 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -192,7 +192,19 @@ impl FormatRule> for FormatSuite { } } } else if is_import_definition(preceding) && !is_import_definition(following) { - empty_line().fmt(f)?; + // Enforce _at least_ one empty line after an import statement (but allow up to + // two at the top-level). + match self.kind { + SuiteKind::TopLevel => { + match lines_after_ignoring_trivia(preceding.end(), source) { + 0 | 1 | 2 => empty_line().fmt(f)?, + _ => write!(f, [empty_line(), empty_line()])?, + } + } + SuiteKind::Function | SuiteKind::Class | SuiteKind::Other => { + empty_line().fmt(f)?; + } + } } else if is_compound_statement(preceding) { // Handles the case where a body has trailing comments. The issue is that RustPython does not include // the comments in the range of the suite. This means, the body ends right after the last statement in the body. diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__import.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__import.py.snap index 4c76352286..5b3177fc5d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__import.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__import.py.snap @@ -7,6 +7,61 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/ from a import aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa from a import aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa, aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa from a import aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa as dfgsdfgsd, aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa as sdkjflsdjlahlfd + +# At the top-level, force one empty line after an import, but allow up to two empty +# lines. +import os +import sys +x = 1 + +import os +import sys + +x = 1 + +import os +import sys + + +x = 1 + +import os +import sys + + + +x = 1 + + +# In a nested scope, force one empty line after an import. +def func(): + import os + import sys + x = 1 + + +def func(): + import os + import sys + + x = 1 + + +def func(): + import os + import sys + + + x = 1 + + +def func(): + import os + import sys + + + + x = 1 ``` ## Output @@ -22,6 +77,59 @@ from a import ( aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa as dfgsdfgsd, aksjdhflsakhdflkjsadlfajkslhfdkjsaldajlahflashdfljahlfksajlhfajfjfsaahflakjslhdfkjalhdskjfa as sdkjflsdjlahlfd, ) + +# At the top-level, force one empty line after an import, but allow up to two empty +# lines. +import os +import sys + +x = 1 + +import os +import sys + +x = 1 + +import os +import sys + + +x = 1 + +import os +import sys + + +x = 1 + + +# In a nested scope, force one empty line after an import. +def func(): + import os + import sys + + x = 1 + + +def func(): + import os + import sys + + x = 1 + + +def func(): + import os + import sys + + x = 1 + + +def func(): + import os + import sys + + x = 1 ```