mirror of https://github.com/astral-sh/ruff
Insert empty line between suite and alternative branch after def/class (#12294)
When there is a function or class definition at the end of a suite
followed by the beginning of an alternative block, we have to insert a
single empty line between them.
In the if-else-statement example below, we insert an empty line after
the `foo` in the if-block, but none after the else-block `foo`, since in
the latter case the enclosing suite already adds empty lines.
```python
if sys.version_info >= (3, 10):
def foo():
return "new"
else:
def foo():
return "old"
class Bar:
pass
```
To do so, we track whether the current suite is the last one in the
current statement with a new option on the suite kind.
Fixes #12199
---------
Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
ecd4b4d943
commit
9a817a2922
|
|
@ -249,6 +249,68 @@ if True:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if True:
|
||||||
|
# fmt: off
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
# fmt: on
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
match True:
|
||||||
|
case 1:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
case 1:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except RuntimeError:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except RuntimeError:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except ZeroDivisionError:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if raw:
|
||||||
|
def show_file(lines):
|
||||||
|
for line in lines:
|
||||||
|
pass
|
||||||
|
# Trailing comment not on function or class
|
||||||
|
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Please keep this the last block in this file. This tests that we don't insert
|
# NOTE: Please keep this the last block in this file. This tests that we don't insert
|
||||||
# empty line(s) at the end of the file due to nested function
|
# empty line(s) at the end of the file due to nested function
|
||||||
if True:
|
if True:
|
||||||
|
|
|
||||||
|
|
@ -154,8 +154,14 @@ def f():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# comment
|
# comment
|
||||||
|
|
||||||
x = 1
|
x = 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use ruff_python_ast::ElifElseClause;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::stmt_if::format_elif_else_clause;
|
use crate::statement::stmt_if::format_elif_else_clause;
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
/// Note that this implementation misses the leading newlines before the leading comments because
|
/// Note that this implementation misses the leading newlines before the leading comments because
|
||||||
/// it does not have access to the last node of the previous branch. The `StmtIf` therefore doesn't
|
/// it does not have access to the last node of the previous branch. The `StmtIf` therefore doesn't
|
||||||
|
|
@ -11,6 +12,15 @@ pub struct FormatElifElseClause;
|
||||||
|
|
||||||
impl FormatNodeRule<ElifElseClause> for FormatElifElseClause {
|
impl FormatNodeRule<ElifElseClause> for FormatElifElseClause {
|
||||||
fn fmt_fields(&self, item: &ElifElseClause, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ElifElseClause, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
format_elif_else_clause(item, f, None)
|
format_elif_else_clause(
|
||||||
|
item,
|
||||||
|
f,
|
||||||
|
None,
|
||||||
|
SuiteKind::Other {
|
||||||
|
// For stability, we can't insert an empty line if we don't know if the outer suite
|
||||||
|
// also does.
|
||||||
|
last_suite_in_statement: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@ use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub enum ExceptHandlerKind {
|
pub(crate) enum ExceptHandlerKind {
|
||||||
#[default]
|
#[default]
|
||||||
Regular,
|
Regular,
|
||||||
Starred,
|
Starred,
|
||||||
|
|
@ -16,16 +17,18 @@ pub enum ExceptHandlerKind {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatExceptHandlerExceptHandler {
|
pub struct FormatExceptHandlerExceptHandler {
|
||||||
except_handler_kind: ExceptHandlerKind,
|
pub(crate) except_handler_kind: ExceptHandlerKind,
|
||||||
|
pub(crate) last_suite_in_statement: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatRuleWithOptions<ExceptHandlerExceptHandler, PyFormatContext<'_>>
|
impl FormatRuleWithOptions<ExceptHandlerExceptHandler, PyFormatContext<'_>>
|
||||||
for FormatExceptHandlerExceptHandler
|
for FormatExceptHandlerExceptHandler
|
||||||
{
|
{
|
||||||
type Options = ExceptHandlerKind;
|
type Options = FormatExceptHandlerExceptHandler;
|
||||||
|
|
||||||
fn with_options(mut self, options: Self::Options) -> Self {
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
self.except_handler_kind = options;
|
self.except_handler_kind = options.except_handler_kind;
|
||||||
|
self.last_suite_in_statement = options.last_suite_in_statement;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +39,7 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
|
||||||
item: &ExceptHandlerExceptHandler,
|
item: &ExceptHandlerExceptHandler,
|
||||||
f: &mut PyFormatter,
|
f: &mut PyFormatter,
|
||||||
) -> FormatResult<()> {
|
) -> FormatResult<()> {
|
||||||
|
let except_handler_kind = self.except_handler_kind;
|
||||||
let ExceptHandlerExceptHandler {
|
let ExceptHandlerExceptHandler {
|
||||||
range: _,
|
range: _,
|
||||||
type_,
|
type_,
|
||||||
|
|
@ -57,7 +61,7 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
token("except"),
|
token("except"),
|
||||||
match self.except_handler_kind {
|
match except_handler_kind {
|
||||||
ExceptHandlerKind::Regular => None,
|
ExceptHandlerKind::Regular => None,
|
||||||
ExceptHandlerKind::Starred => Some(token("*")),
|
ExceptHandlerKind::Starred => Some(token("*")),
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +88,11 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
clause_body(body, dangling_comments),
|
clause_body(
|
||||||
|
body,
|
||||||
|
SuiteKind::other(self.last_suite_in_statement),
|
||||||
|
dangling_comments
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_formatter::write;
|
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||||
use ruff_python_ast::AstNode;
|
use ruff_python_ast::AstNode;
|
||||||
use ruff_python_ast::MatchCase;
|
use ruff_python_ast::MatchCase;
|
||||||
|
|
||||||
|
|
@ -6,9 +6,21 @@ use crate::builders::parenthesize_if_expands;
|
||||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parentheses};
|
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parentheses};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatMatchCase;
|
pub struct FormatMatchCase {
|
||||||
|
last_suite_in_statement: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRuleWithOptions<MatchCase, PyFormatContext<'_>> for FormatMatchCase {
|
||||||
|
type Options = bool;
|
||||||
|
|
||||||
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
|
self.last_suite_in_statement = options;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatNodeRule<MatchCase> for FormatMatchCase {
|
impl FormatNodeRule<MatchCase> for FormatMatchCase {
|
||||||
fn fmt_fields(&self, item: &MatchCase, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &MatchCase, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
|
@ -63,7 +75,11 @@ impl FormatNodeRule<MatchCase> for FormatMatchCase {
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
clause_body(body, dangling_item_comments),
|
clause_body(
|
||||||
|
body,
|
||||||
|
SuiteKind::other(self.last_suite_in_statement),
|
||||||
|
dangling_item_comments
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -380,21 +380,14 @@ pub(crate) struct FormatClauseBody<'a> {
|
||||||
trailing_comments: &'a [SourceComment],
|
trailing_comments: &'a [SourceComment],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormatClauseBody<'a> {
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn with_kind(mut self, kind: SuiteKind) -> Self {
|
|
||||||
self.kind = kind;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn clause_body<'a>(
|
pub(crate) fn clause_body<'a>(
|
||||||
body: &'a Suite,
|
body: &'a Suite,
|
||||||
|
kind: SuiteKind,
|
||||||
trailing_comments: &'a [SourceComment],
|
trailing_comments: &'a [SourceComment],
|
||||||
) -> FormatClauseBody<'a> {
|
) -> FormatClauseBody<'a> {
|
||||||
FormatClauseBody {
|
FormatClauseBody {
|
||||||
body,
|
body,
|
||||||
kind: SuiteKind::default(),
|
kind,
|
||||||
trailing_comments,
|
trailing_comments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
clause_body(body, trailing_definition_comments).with_kind(SuiteKind::Class),
|
clause_body(body, SuiteKind::Class, trailing_definition_comments),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ExprTupleWithoutParentheses<'a>(&'a Expr);
|
struct ExprTupleWithoutParentheses<'a>(&'a Expr);
|
||||||
|
|
@ -63,7 +64,11 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
|
||||||
maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks),
|
maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
clause_body(body, trailing_condition_comments),
|
clause_body(
|
||||||
|
body,
|
||||||
|
SuiteKind::other(orelse.is_empty()),
|
||||||
|
trailing_condition_comments
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -85,7 +90,7 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
|
||||||
&token("else"),
|
&token("else"),
|
||||||
)
|
)
|
||||||
.with_leading_comments(leading, body.last()),
|
.with_leading_comments(leading, body.last()),
|
||||||
clause_body(orelse, trailing),
|
clause_body(orelse, SuiteKind::other(true), trailing),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ impl FormatNodeRule<StmtFunctionDef> for FormatStmtFunctionDef {
|
||||||
trailing_definition_comments,
|
trailing_definition_comments,
|
||||||
&format_with(|f| format_function_header(f, item)),
|
&format_with(|f| format_function_header(f, item)),
|
||||||
),
|
),
|
||||||
clause_body(body, trailing_definition_comments).with_kind(SuiteKind::Function),
|
clause_body(body, SuiteKind::Function, trailing_definition_comments),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use ruff_formatter::{format_args, write};
|
use ruff_formatter::{format_args, write};
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::{AnyNodeRef, ElifElseClause, StmtIf};
|
||||||
use ruff_python_ast::{ElifElseClause, StmtIf};
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatStmtIf;
|
pub struct FormatStmtIf;
|
||||||
|
|
@ -35,13 +35,22 @@ impl FormatNodeRule<StmtIf> for FormatStmtIf {
|
||||||
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
clause_body(body, trailing_colon_comment),
|
clause_body(
|
||||||
|
body,
|
||||||
|
SuiteKind::other(elif_else_clauses.is_empty()),
|
||||||
|
trailing_colon_comment
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut last_node = body.last().unwrap().into();
|
let mut last_node = body.last().unwrap().into();
|
||||||
for clause in elif_else_clauses {
|
for clause in elif_else_clauses {
|
||||||
format_elif_else_clause(clause, f, Some(last_node))?;
|
format_elif_else_clause(
|
||||||
|
clause,
|
||||||
|
f,
|
||||||
|
Some(last_node),
|
||||||
|
SuiteKind::other(clause == elif_else_clauses.last().unwrap()),
|
||||||
|
)?;
|
||||||
last_node = clause.body.last().unwrap().into();
|
last_node = clause.body.last().unwrap().into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +64,7 @@ pub(crate) fn format_elif_else_clause(
|
||||||
item: &ElifElseClause,
|
item: &ElifElseClause,
|
||||||
f: &mut PyFormatter,
|
f: &mut PyFormatter,
|
||||||
last_node: Option<AnyNodeRef>,
|
last_node: Option<AnyNodeRef>,
|
||||||
|
suite_kind: SuiteKind,
|
||||||
) -> FormatResult<()> {
|
) -> FormatResult<()> {
|
||||||
let ElifElseClause {
|
let ElifElseClause {
|
||||||
range: _,
|
range: _,
|
||||||
|
|
@ -93,7 +103,7 @@ pub(crate) fn format_elif_else_clause(
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.with_leading_comments(leading_comments, last_node),
|
.with_leading_comments(leading_comments, last_node),
|
||||||
clause_body(body, trailing_colon_comment),
|
clause_body(body, suite_kind, trailing_colon_comment),
|
||||||
f.options()
|
f.options()
|
||||||
.source_map_generation()
|
.source_map_generation()
|
||||||
.is_enabled()
|
.is_enabled()
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ impl FormatNodeRule<StmtMatch> for FormatStmtMatch {
|
||||||
let mut last_case = first;
|
let mut last_case = first;
|
||||||
|
|
||||||
for case in cases_iter {
|
for case in cases_iter {
|
||||||
|
let last_suite_in_statement = Some(case) == cases.last();
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[block_indent(&format_args!(
|
[block_indent(&format_args!(
|
||||||
|
|
@ -55,7 +56,7 @@ impl FormatNodeRule<StmtMatch> for FormatStmtMatch {
|
||||||
comments.leading(case),
|
comments.leading(case),
|
||||||
last_case.body.last(),
|
last_case.body.last(),
|
||||||
),
|
),
|
||||||
case.format()
|
case.format().with_options(last_suite_in_statement)
|
||||||
))]
|
))]
|
||||||
)?;
|
)?;
|
||||||
last_case = case;
|
last_case = case;
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,12 @@ use ruff_text_size::Ranged;
|
||||||
use crate::comments;
|
use crate::comments;
|
||||||
use crate::comments::leading_alternate_branch_comments;
|
use crate::comments::leading_alternate_branch_comments;
|
||||||
use crate::comments::SourceComment;
|
use crate::comments::SourceComment;
|
||||||
use crate::other::except_handler_except_handler::ExceptHandlerKind;
|
use crate::other::except_handler_except_handler::{
|
||||||
|
ExceptHandlerKind, FormatExceptHandlerExceptHandler,
|
||||||
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
use crate::statement::{FormatRefWithRule, Stmt};
|
use crate::statement::{FormatRefWithRule, Stmt};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -16,13 +19,15 @@ pub struct FormatStmtTry;
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub struct FormatExceptHandler {
|
pub struct FormatExceptHandler {
|
||||||
except_handler_kind: ExceptHandlerKind,
|
except_handler_kind: ExceptHandlerKind,
|
||||||
|
last_suite_in_statement: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FormatRuleWithOptions<ExceptHandler, PyFormatContext<'_>> for FormatExceptHandler {
|
impl FormatRuleWithOptions<ExceptHandler, PyFormatContext<'_>> for FormatExceptHandler {
|
||||||
type Options = ExceptHandlerKind;
|
type Options = FormatExceptHandler;
|
||||||
|
|
||||||
fn with_options(mut self, options: Self::Options) -> Self {
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
self.except_handler_kind = options;
|
self.except_handler_kind = options.except_handler_kind;
|
||||||
|
self.last_suite_in_statement = options.last_suite_in_statement;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +37,10 @@ impl FormatRule<ExceptHandler, PyFormatContext<'_>> for FormatExceptHandler {
|
||||||
match item {
|
match item {
|
||||||
ExceptHandler::ExceptHandler(except_handler) => except_handler
|
ExceptHandler::ExceptHandler(except_handler) => except_handler
|
||||||
.format()
|
.format()
|
||||||
.with_options(self.except_handler_kind)
|
.with_options(FormatExceptHandlerExceptHandler {
|
||||||
|
except_handler_kind: self.except_handler_kind,
|
||||||
|
last_suite_in_statement: self.last_suite_in_statement,
|
||||||
|
})
|
||||||
.fmt(f),
|
.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,8 +64,8 @@ impl FormatNodeRule<StmtTry> for FormatStmtTry {
|
||||||
let StmtTry {
|
let StmtTry {
|
||||||
body,
|
body,
|
||||||
handlers,
|
handlers,
|
||||||
orelse: _,
|
orelse,
|
||||||
finalbody: _,
|
finalbody,
|
||||||
is_star,
|
is_star,
|
||||||
range: _,
|
range: _,
|
||||||
} = item;
|
} = item;
|
||||||
|
|
@ -65,31 +73,51 @@ impl FormatNodeRule<StmtTry> for FormatStmtTry {
|
||||||
let comments_info = f.context().comments().clone();
|
let comments_info = f.context().comments().clone();
|
||||||
let mut dangling_comments = comments_info.dangling(item);
|
let mut dangling_comments = comments_info.dangling(item);
|
||||||
|
|
||||||
(_, dangling_comments) = format_case(item, CaseKind::Try, None, dangling_comments, f)?;
|
(_, dangling_comments) =
|
||||||
|
format_case(item, CaseKind::Try, None, dangling_comments, false, f)?;
|
||||||
let mut previous_node = body.last();
|
let mut previous_node = body.last();
|
||||||
|
|
||||||
for handler in handlers {
|
for handler in handlers {
|
||||||
let handler_comments = comments_info.leading(handler);
|
let handler_comments = comments_info.leading(handler);
|
||||||
|
let ExceptHandler::ExceptHandler(except_handler) = handler;
|
||||||
|
let except_handler_kind = if *is_star {
|
||||||
|
ExceptHandlerKind::Starred
|
||||||
|
} else {
|
||||||
|
ExceptHandlerKind::Regular
|
||||||
|
};
|
||||||
|
let last_suite_in_statement =
|
||||||
|
handler == handlers.last().unwrap() && orelse.is_empty() && finalbody.is_empty();
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
leading_alternate_branch_comments(handler_comments, previous_node),
|
leading_alternate_branch_comments(handler_comments, previous_node),
|
||||||
&handler.format().with_options(if *is_star {
|
&handler.format().with_options(FormatExceptHandler {
|
||||||
ExceptHandlerKind::Starred
|
except_handler_kind,
|
||||||
} else {
|
last_suite_in_statement
|
||||||
ExceptHandlerKind::Regular
|
})
|
||||||
}),
|
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
previous_node = match handler {
|
previous_node = except_handler.body.last();
|
||||||
ExceptHandler::ExceptHandler(handler) => handler.body.last(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(previous_node, dangling_comments) =
|
(previous_node, dangling_comments) = format_case(
|
||||||
format_case(item, CaseKind::Else, previous_node, dangling_comments, f)?;
|
item,
|
||||||
|
CaseKind::Else,
|
||||||
|
previous_node,
|
||||||
|
dangling_comments,
|
||||||
|
finalbody.is_empty(),
|
||||||
|
f,
|
||||||
|
)?;
|
||||||
|
|
||||||
format_case(item, CaseKind::Finally, previous_node, dangling_comments, f)?;
|
format_case(
|
||||||
|
item,
|
||||||
|
CaseKind::Finally,
|
||||||
|
previous_node,
|
||||||
|
dangling_comments,
|
||||||
|
true,
|
||||||
|
f,
|
||||||
|
)?;
|
||||||
|
|
||||||
write!(f, [comments::dangling_comments(dangling_comments)])
|
write!(f, [comments::dangling_comments(dangling_comments)])
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +128,7 @@ fn format_case<'a>(
|
||||||
kind: CaseKind,
|
kind: CaseKind,
|
||||||
previous_node: Option<&'a Stmt>,
|
previous_node: Option<&'a Stmt>,
|
||||||
dangling_comments: &'a [SourceComment],
|
dangling_comments: &'a [SourceComment],
|
||||||
|
last_suite_in_statement: bool,
|
||||||
f: &mut PyFormatter,
|
f: &mut PyFormatter,
|
||||||
) -> FormatResult<(Option<&'a Stmt>, &'a [SourceComment])> {
|
) -> FormatResult<(Option<&'a Stmt>, &'a [SourceComment])> {
|
||||||
let body = match kind {
|
let body = match kind {
|
||||||
|
|
@ -129,7 +158,11 @@ fn format_case<'a>(
|
||||||
[
|
[
|
||||||
clause_header(header, trailing_case_comments, &token(kind.keyword()))
|
clause_header(header, trailing_case_comments, &token(kind.keyword()))
|
||||||
.with_leading_comments(leading_case_comments, previous_node),
|
.with_leading_comments(leading_case_comments, previous_node),
|
||||||
clause_body(body, trailing_case_comments),
|
clause_body(
|
||||||
|
body,
|
||||||
|
SuiteKind::other(last_suite_in_statement),
|
||||||
|
trailing_case_comments
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
(Some(last), rest)
|
(Some(last), rest)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatStmtWhile;
|
pub struct FormatStmtWhile;
|
||||||
|
|
@ -42,7 +43,11 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
||||||
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
clause_body(body, trailing_condition_comments),
|
clause_body(
|
||||||
|
body,
|
||||||
|
SuiteKind::other(orelse.is_empty()),
|
||||||
|
trailing_condition_comments
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -62,7 +67,7 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
||||||
&token("else")
|
&token("else")
|
||||||
)
|
)
|
||||||
.with_leading_comments(leading, body.last()),
|
.with_leading_comments(leading, body.last()),
|
||||||
clause_body(orelse, trailing),
|
clause_body(orelse, SuiteKind::other(true), trailing),
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use crate::other::commas;
|
||||||
use crate::other::with_item::WithItemLayout;
|
use crate::other::with_item::WithItemLayout;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
||||||
|
use crate::statement::suite::SuiteKind;
|
||||||
use crate::PythonVersion;
|
use crate::PythonVersion;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -124,7 +125,7 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
clause_body(&with_stmt.body, colon_comments)
|
clause_body(&with_stmt.body, SuiteKind::other(true), colon_comments)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ use crate::verbatim::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Level at which the [`Suite`] appears in the source code.
|
/// Level at which the [`Suite`] appears in the source code.
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum SuiteKind {
|
pub enum SuiteKind {
|
||||||
/// Statements at the module level / top level
|
/// Statements at the module level / top level
|
||||||
TopLevel,
|
TopLevel,
|
||||||
|
|
@ -32,23 +32,62 @@ pub enum SuiteKind {
|
||||||
Class,
|
Class,
|
||||||
|
|
||||||
/// Statements in any other body (e.g., `if` or `while`).
|
/// Statements in any other body (e.g., `if` or `while`).
|
||||||
#[default]
|
Other {
|
||||||
Other,
|
/// Whether this suite is the last suite in the current statement.
|
||||||
|
///
|
||||||
|
/// Below, `last_suite_in_statement` is `false` for the suite containing `foo10` and `foo12`
|
||||||
|
/// and `true` for the suite containing `bar`.
|
||||||
|
/// ```python
|
||||||
|
/// if sys.version_info >= (3, 10):
|
||||||
|
/// def foo10():
|
||||||
|
/// return "new"
|
||||||
|
/// elif sys.version_info >= (3, 12):
|
||||||
|
/// def foo12():
|
||||||
|
/// return "new"
|
||||||
|
/// else:
|
||||||
|
/// def bar():
|
||||||
|
/// return "old"
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// When this value is true, we don't insert trailing empty lines since the containing suite
|
||||||
|
/// will do that.
|
||||||
|
last_suite_in_statement: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl Default for SuiteKind {
|
||||||
pub struct FormatSuite {
|
|
||||||
kind: SuiteKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for FormatSuite {
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FormatSuite {
|
Self::Other {
|
||||||
kind: SuiteKind::Other,
|
// For stability, we can't insert an empty line if we don't know if the outer suite
|
||||||
|
// also does.
|
||||||
|
last_suite_in_statement: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SuiteKind {
|
||||||
|
/// See [`SuiteKind::Other`].
|
||||||
|
pub fn other(last_suite_in_statement: bool) -> Self {
|
||||||
|
Self::Other {
|
||||||
|
last_suite_in_statement,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_suite_in_statement(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Other {
|
||||||
|
last_suite_in_statement,
|
||||||
|
} => last_suite_in_statement,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct FormatSuite {
|
||||||
|
kind: SuiteKind,
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
fn fmt(&self, statements: &Suite, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt(&self, statements: &Suite, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
let mut iter = statements.iter();
|
let mut iter = statements.iter();
|
||||||
|
|
@ -64,7 +103,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
TopLevelStatementPosition::Other
|
TopLevelStatementPosition::Other
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other => {
|
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other { .. } => {
|
||||||
NodeLevel::CompoundStatement
|
NodeLevel::CompoundStatement
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -78,7 +117,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
|
|
||||||
// Format the first statement in the body, which often has special formatting rules.
|
// Format the first statement in the body, which often has special formatting rules.
|
||||||
let first = match self.kind {
|
let first = match self.kind {
|
||||||
SuiteKind::Other => {
|
SuiteKind::Other { .. } => {
|
||||||
if is_class_or_function_definition(first)
|
if is_class_or_function_definition(first)
|
||||||
&& !comments.has_leading(first)
|
&& !comments.has_leading(first)
|
||||||
&& !source_type.is_stub()
|
&& !source_type.is_stub()
|
||||||
|
|
@ -161,55 +200,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
// Here we insert empty lines even if the preceding has a trailing own line comment
|
// Here we insert empty lines even if the preceding has a trailing own line comment
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
// Find nested class or function definitions that need an empty line after them.
|
trailing_function_or_class_def(Some(preceding), &comments).is_some()
|
||||||
//
|
|
||||||
// ```python
|
|
||||||
// def f():
|
|
||||||
// if True:
|
|
||||||
//
|
|
||||||
// def double(s):
|
|
||||||
// return s + s
|
|
||||||
//
|
|
||||||
// print("below function")
|
|
||||||
// ```
|
|
||||||
std::iter::successors(
|
|
||||||
Some(AnyNodeRef::from(preceding)),
|
|
||||||
AnyNodeRef::last_child_in_body,
|
|
||||||
)
|
|
||||||
.take_while(|last_child|
|
|
||||||
// If there is a comment between preceding and following the empty lines were
|
|
||||||
// inserted before the comment by preceding and there are no extra empty lines
|
|
||||||
// after the comment.
|
|
||||||
// ```python
|
|
||||||
// class Test:
|
|
||||||
// def a(self):
|
|
||||||
// pass
|
|
||||||
// # trailing comment
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// # two lines before, one line after
|
|
||||||
//
|
|
||||||
// c = 30
|
|
||||||
// ````
|
|
||||||
// This also includes nested class/function definitions, so we stop recursing
|
|
||||||
// once we see a node with a trailing own line comment:
|
|
||||||
// ```python
|
|
||||||
// def f():
|
|
||||||
// if True:
|
|
||||||
//
|
|
||||||
// def double(s):
|
|
||||||
// return s + s
|
|
||||||
//
|
|
||||||
// # nested trailing own line comment
|
|
||||||
// print("below function with trailing own line comment")
|
|
||||||
// ```
|
|
||||||
!comments.has_trailing_own_line(*last_child))
|
|
||||||
.any(|last_child| {
|
|
||||||
matches!(
|
|
||||||
last_child,
|
|
||||||
AnyNodeRef::StmtFunctionDef(_) | AnyNodeRef::StmtClassDef(_)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add empty lines before and after a function or class definition. If the preceding
|
// Add empty lines before and after a function or class definition. If the preceding
|
||||||
|
|
@ -248,7 +239,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
SuiteKind::TopLevel => {
|
SuiteKind::TopLevel => {
|
||||||
write!(f, [empty_line(), empty_line()])?;
|
write!(f, [empty_line(), empty_line()])?;
|
||||||
}
|
}
|
||||||
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other => {
|
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other { .. } => {
|
||||||
empty_line().fmt(f)?;
|
empty_line().fmt(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +271,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other => {
|
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other { .. } => {
|
||||||
empty_line().fmt(f)?;
|
empty_line().fmt(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -319,7 +310,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
write!(f, [empty_line(), empty_line()])?;
|
write!(f, [empty_line(), empty_line()])?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other => {
|
SuiteKind::Function | SuiteKind::Class | SuiteKind::Other { .. } => {
|
||||||
empty_line().fmt(f)?;
|
empty_line().fmt(f)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -413,10 +404,129 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
||||||
empty_line_after_docstring = false;
|
empty_line_after_docstring = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.between_alternative_blocks_empty_line(statements, &comments, f)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FormatSuite {
|
||||||
|
/// Add an empty line between a function or class and an alternative body.
|
||||||
|
///
|
||||||
|
/// We only insert an empty if we're between suites in a multi-suite statement. In the
|
||||||
|
/// if-else-statement below, we insert an empty line after the `foo` in the if-block, but none
|
||||||
|
/// after the else-block `foo`, since in the latter case the enclosing suite already adds
|
||||||
|
/// empty lines.
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// if sys.version_info >= (3, 10):
|
||||||
|
/// def foo():
|
||||||
|
/// return "new"
|
||||||
|
/// else:
|
||||||
|
/// def foo():
|
||||||
|
/// return "old"
|
||||||
|
/// class Bar:
|
||||||
|
/// pass
|
||||||
|
/// ```
|
||||||
|
fn between_alternative_blocks_empty_line(
|
||||||
|
&self,
|
||||||
|
statements: &Suite,
|
||||||
|
comments: &Comments,
|
||||||
|
f: &mut PyFormatter,
|
||||||
|
) -> FormatResult<()> {
|
||||||
|
if self.kind.last_suite_in_statement() {
|
||||||
|
// If we're at the end of the current statement, the outer suite will insert one or
|
||||||
|
// two empty lines already.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(last_def_or_class) = trailing_function_or_class_def(statements.last(), comments)
|
||||||
|
else {
|
||||||
|
// An empty line is only inserted for function and class definitions.
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip the last trailing own line comment of the suite, if any, otherwise we count
|
||||||
|
// the lines wrongly by stopping at that comment.
|
||||||
|
let node_with_last_trailing_comment = std::iter::successors(
|
||||||
|
statements.last().map(AnyNodeRef::from),
|
||||||
|
AnyNodeRef::last_child_in_body,
|
||||||
|
)
|
||||||
|
.find(|last_child| comments.has_trailing_own_line(*last_child));
|
||||||
|
|
||||||
|
let end_of_def_or_class = node_with_last_trailing_comment
|
||||||
|
.and_then(|child| comments.trailing(child).last().map(Ranged::end))
|
||||||
|
.unwrap_or(last_def_or_class.end());
|
||||||
|
let existing_newlines =
|
||||||
|
lines_after_ignoring_end_of_line_trivia(end_of_def_or_class, f.context().source());
|
||||||
|
if existing_newlines < 2 {
|
||||||
|
if f.context().is_preview() {
|
||||||
|
empty_line().fmt(f)?;
|
||||||
|
} else {
|
||||||
|
if last_def_or_class.is_stmt_class_def() && f.options().source_type().is_stub() {
|
||||||
|
empty_line().fmt(f)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find nested class or function definitions that need an empty line after them.
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// def f():
|
||||||
|
/// if True:
|
||||||
|
///
|
||||||
|
/// def double(s):
|
||||||
|
/// return s + s
|
||||||
|
///
|
||||||
|
/// print("below function")
|
||||||
|
/// ```
|
||||||
|
fn trailing_function_or_class_def<'a>(
|
||||||
|
preceding: Option<&'a Stmt>,
|
||||||
|
comments: &Comments,
|
||||||
|
) -> Option<AnyNodeRef<'a>> {
|
||||||
|
std::iter::successors(
|
||||||
|
preceding.map(AnyNodeRef::from),
|
||||||
|
AnyNodeRef::last_child_in_body,
|
||||||
|
)
|
||||||
|
.take_while(|last_child|
|
||||||
|
// If there is a comment between preceding and following the empty lines were
|
||||||
|
// inserted before the comment by preceding and there are no extra empty lines
|
||||||
|
// after the comment.
|
||||||
|
// ```python
|
||||||
|
// class Test:
|
||||||
|
// def a(self):
|
||||||
|
// pass
|
||||||
|
// # trailing comment
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// # two lines before, one line after
|
||||||
|
//
|
||||||
|
// c = 30
|
||||||
|
// ````
|
||||||
|
// This also includes nested class/function definitions, so we stop recursing
|
||||||
|
// once we see a node with a trailing own line comment:
|
||||||
|
// ```python
|
||||||
|
// def f():
|
||||||
|
// if True:
|
||||||
|
//
|
||||||
|
// def double(s):
|
||||||
|
// return s + s
|
||||||
|
//
|
||||||
|
// # nested trailing own line comment
|
||||||
|
// print("below function with trailing own line comment")
|
||||||
|
// ```
|
||||||
|
!comments.has_trailing_own_line(*last_child))
|
||||||
|
.find(|last_child| {
|
||||||
|
matches!(
|
||||||
|
last_child,
|
||||||
|
AnyNodeRef::StmtFunctionDef(_) | AnyNodeRef::StmtClassDef(_)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Stub files have bespoke rules for empty lines.
|
/// Stub files have bespoke rules for empty lines.
|
||||||
///
|
///
|
||||||
/// These rules are ported from black (preview mode at time of writing) using the stubs test case:
|
/// These rules are ported from black (preview mode at time of writing) using the stubs test case:
|
||||||
|
|
@ -447,7 +557,7 @@ fn stub_file_empty_lines(
|
||||||
hard_line_break().fmt(f)
|
hard_line_break().fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SuiteKind::Class | SuiteKind::Other | SuiteKind::Function => {
|
SuiteKind::Class | SuiteKind::Other { .. } | SuiteKind::Function => {
|
||||||
if (empty_line_condition
|
if (empty_line_condition
|
||||||
&& lines_after_ignoring_end_of_line_trivia(preceding.end(), source) > 1)
|
&& lines_after_ignoring_end_of_line_trivia(preceding.end(), source) > 1)
|
||||||
|| require_empty_line
|
|| require_empty_line
|
||||||
|
|
@ -477,26 +587,14 @@ pub(crate) fn should_insert_blank_line_after_class_in_stub_file(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Some(following) = following else {
|
||||||
|
// We handle newlines at the end of a suite in `between_alternative_blocks_empty_line`.
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
let comments = context.comments();
|
let comments = context.comments();
|
||||||
match preceding.as_stmt_class_def() {
|
match preceding.as_stmt_class_def() {
|
||||||
Some(class) if contains_only_an_ellipsis(&class.body, comments) => {
|
Some(class) if contains_only_an_ellipsis(&class.body, comments) => {
|
||||||
let Some(following) = following else {
|
|
||||||
// The formatter is at the start of an alternate branch such as
|
|
||||||
// an `else` block.
|
|
||||||
//
|
|
||||||
// ```python
|
|
||||||
// if foo:
|
|
||||||
// class Nested:
|
|
||||||
// pass
|
|
||||||
// else:
|
|
||||||
// pass
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// In the above code, the preceding node is the `Nested` class
|
|
||||||
// which has no following node.
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the preceding class has decorators, then we need to add an empty
|
// If the preceding class has decorators, then we need to add an empty
|
||||||
// line even if it only contains ellipsis.
|
// line even if it only contains ellipsis.
|
||||||
//
|
//
|
||||||
|
|
@ -916,7 +1014,9 @@ def trailing_func():
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_level() {
|
fn nested_level() {
|
||||||
let formatted = format_suite(SuiteKind::Other);
|
let formatted = format_suite(SuiteKind::Other {
|
||||||
|
last_suite_in_statement: true,
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
formatted,
|
formatted,
|
||||||
|
|
|
||||||
|
|
@ -505,5 +505,3 @@ def foo():
|
||||||
def bar():
|
def bar():
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -220,5 +220,3 @@ else:
|
||||||
with hmm_but_this_should_get_two_preceding_newlines():
|
with hmm_but_this_should_get_two_preceding_newlines():
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,5 +255,3 @@ class Conditional:
|
||||||
def l(self): ...
|
def l(self): ...
|
||||||
def m(self): ...
|
def m(self): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,68 @@ if True:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if True:
|
||||||
|
# fmt: off
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
# fmt: on
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
match True:
|
||||||
|
case 1:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
case 1:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except RuntimeError:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except RuntimeError:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except ZeroDivisionError:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if raw:
|
||||||
|
def show_file(lines):
|
||||||
|
for line in lines:
|
||||||
|
pass
|
||||||
|
# Trailing comment not on function or class
|
||||||
|
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Please keep this the last block in this file. This tests that we don't insert
|
# NOTE: Please keep this the last block in this file. This tests that we don't insert
|
||||||
# empty line(s) at the end of the file due to nested function
|
# empty line(s) at the end of the file due to nested function
|
||||||
if True:
|
if True:
|
||||||
|
|
@ -558,6 +620,85 @@ if True:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if True:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if True:
|
||||||
|
# fmt: off
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
# fmt: on
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
match True:
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except RuntimeError:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except RuntimeError:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
except ZeroDivisionError:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if raw:
|
||||||
|
|
||||||
|
def show_file(lines):
|
||||||
|
for line in lines:
|
||||||
|
pass
|
||||||
|
# Trailing comment not on function or class
|
||||||
|
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Please keep this the last block in this file. This tests that we don't insert
|
# NOTE: Please keep this the last block in this file. This tests that we don't insert
|
||||||
# empty line(s) at the end of the file due to nested function
|
# empty line(s) at the end of the file due to nested function
|
||||||
if True:
|
if True:
|
||||||
|
|
@ -593,4 +734,63 @@ def overload4(a: int): ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -277,6 +277,7 @@
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@@ -293,6 +294,7 @@
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
@@ -303,6 +305,7 @@
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
except RuntimeError:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
@@ -313,6 +316,7 @@
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
finally:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
@@ -323,18 +327,22 @@
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
except RuntimeError:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
except ZeroDivisionError:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
finally:
|
||||||
|
|
||||||
|
def a():
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -160,11 +160,17 @@ def f():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# comment
|
# comment
|
||||||
|
|
||||||
x = 1
|
x = 1
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
|
|
@ -302,10 +308,28 @@ x = 1
|
||||||
def f():
|
def f():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if True:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
# comment
|
# comment
|
||||||
|
|
||||||
x = 1
|
x = 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -134,6 +134,7 @@
|
||||||
|
if True:
|
||||||
|
def a():
|
||||||
|
return 1
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1042,3 +1042,26 @@ def func[T](
|
||||||
lotsoflongargs5: T,
|
lotsoflongargs5: T,
|
||||||
) -> T: ...
|
) -> T: ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -161,6 +161,7 @@
|
||||||
|
|
||||||
|
def f1():
|
||||||
|
pass # a
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@@ -170,6 +171,7 @@
|
||||||
|
def f2():
|
||||||
|
pass
|
||||||
|
# a
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -609,4 +609,22 @@ if parent_body:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -93,11 +93,13 @@
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
# 1
|
||||||
|
+
|
||||||
|
elif True:
|
||||||
|
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
# 2
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
|
||||||
|
def f():
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -366,4 +366,28 @@ finally:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -117,16 +117,19 @@
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
# a
|
||||||
|
+
|
||||||
|
except:
|
||||||
|
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
# b
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
# c
|
||||||
|
+
|
||||||
|
finally:
|
||||||
|
|
||||||
|
def f():
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -414,6 +414,3 @@ class Eof:
|
||||||
class Nested:
|
class Nested:
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -311,4 +311,24 @@ class ComplexStatements:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Preview changes
|
||||||
|
```diff
|
||||||
|
--- Stable
|
||||||
|
+++ Preview
|
||||||
|
@@ -132,6 +132,7 @@
|
||||||
|
if sys.version_info >= (3, 12):
|
||||||
|
@classmethod
|
||||||
|
def fromtimestamp(cls, timestamp: float, tz: float | None = ...) -> Self: ...
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
@classmethod
|
||||||
|
def fromtimestamp(cls, __timestamp: float, tz: float | None = ...) -> Self: ...
|
||||||
|
@@ -141,6 +142,7 @@
|
||||||
|
if sys.version_info >= (3, 8):
|
||||||
|
@classmethod
|
||||||
|
def now(cls, tz: float | None = None) -> Self: ...
|
||||||
|
+
|
||||||
|
else:
|
||||||
|
@classmethod
|
||||||
|
def now(cls, tz: None = None) -> Self: ...
|
||||||
|
```
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue