Skip to content

Commit

Permalink
Insert empty line between suite and alternative branch after def/class (
Browse files Browse the repository at this point in the history
#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>
  • Loading branch information
konstin and MichaReiser committed Jul 15, 2024
1 parent ecd4b4d commit 9a817a2
Show file tree
Hide file tree
Showing 25 changed files with 698 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,68 @@ def y():
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
# empty line(s) at the end of the file due to nested function
if True:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,14 @@ def f():
pass


if True:
def a():
return 1
else:
pass


# comment

x = 1

12 changes: 11 additions & 1 deletion crates/ruff_python_formatter/src/other/elif_else_clause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use ruff_python_ast::ElifElseClause;

use crate::prelude::*;
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
/// it does not have access to the last node of the previous branch. The `StmtIf` therefore doesn't
Expand All @@ -11,6 +12,15 @@ pub struct FormatElifElseClause;

impl FormatNodeRule<ElifElseClause> for FormatElifElseClause {
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,
},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,29 @@ use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
use crate::statement::suite::SuiteKind;

#[derive(Copy, Clone, Default)]
pub enum ExceptHandlerKind {
pub(crate) enum ExceptHandlerKind {
#[default]
Regular,
Starred,
}

#[derive(Default)]
pub struct FormatExceptHandlerExceptHandler {
except_handler_kind: ExceptHandlerKind,
pub(crate) except_handler_kind: ExceptHandlerKind,
pub(crate) last_suite_in_statement: bool,
}

impl FormatRuleWithOptions<ExceptHandlerExceptHandler, PyFormatContext<'_>>
for FormatExceptHandlerExceptHandler
{
type Options = ExceptHandlerKind;
type Options = FormatExceptHandlerExceptHandler;

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
}
}
Expand All @@ -36,6 +39,7 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
item: &ExceptHandlerExceptHandler,
f: &mut PyFormatter,
) -> FormatResult<()> {
let except_handler_kind = self.except_handler_kind;
let ExceptHandlerExceptHandler {
range: _,
type_,
Expand All @@ -57,7 +61,7 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
f,
[
token("except"),
match self.except_handler_kind {
match except_handler_kind {
ExceptHandlerKind::Regular => None,
ExceptHandlerKind::Starred => Some(token("*")),
}
Expand All @@ -84,7 +88,11 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
Ok(())
}),
),
clause_body(body, dangling_comments),
clause_body(
body,
SuiteKind::other(self.last_suite_in_statement),
dangling_comments
),
]
)
}
Expand Down
22 changes: 19 additions & 3 deletions crates/ruff_python_formatter/src/other/match_case.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
use ruff_formatter::write;
use ruff_formatter::{write, FormatRuleWithOptions};
use ruff_python_ast::AstNode;
use ruff_python_ast::MatchCase;

use crate::builders::parenthesize_if_expands;
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parentheses};
use crate::prelude::*;
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
use crate::statement::suite::SuiteKind;

#[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 {
fn fmt_fields(&self, item: &MatchCase, f: &mut PyFormatter) -> FormatResult<()> {
Expand Down Expand Up @@ -63,7 +75,11 @@ impl FormatNodeRule<MatchCase> for FormatMatchCase {
Ok(())
}),
),
clause_body(body, dangling_item_comments),
clause_body(
body,
SuiteKind::other(self.last_suite_in_statement),
dangling_item_comments
),
]
)
}
Expand Down
11 changes: 2 additions & 9 deletions crates/ruff_python_formatter/src/statement/clause.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,21 +380,14 @@ pub(crate) struct FormatClauseBody<'a> {
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>(
body: &'a Suite,
kind: SuiteKind,
trailing_comments: &'a [SourceComment],
) -> FormatClauseBody<'a> {
FormatClauseBody {
body,
kind: SuiteKind::default(),
kind,
trailing_comments,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
Ok(())
}),
),
clause_body(body, trailing_definition_comments).with_kind(SuiteKind::Class),
clause_body(body, SuiteKind::Class, trailing_definition_comments),
]
)?;

Expand Down
9 changes: 7 additions & 2 deletions crates/ruff_python_formatter/src/statement/stmt_for.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::statement::clause::{clause_body, clause_header, ClauseHeader, ElseClause};
use crate::statement::suite::SuiteKind;

#[derive(Debug)]
struct ExprTupleWithoutParentheses<'a>(&'a Expr);
Expand Down Expand Up @@ -63,7 +64,11 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks),
],
),
clause_body(body, trailing_condition_comments),
clause_body(
body,
SuiteKind::other(orelse.is_empty()),
trailing_condition_comments
),
]
)?;

Expand All @@ -85,7 +90,7 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
&token("else"),
)
.with_leading_comments(leading, body.last()),
clause_body(orelse, trailing),
clause_body(orelse, SuiteKind::other(true), trailing),
]
)?;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl FormatNodeRule<StmtFunctionDef> for FormatStmtFunctionDef {
trailing_definition_comments,
&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),
]
)?;

Expand Down
20 changes: 15 additions & 5 deletions crates/ruff_python_formatter/src/statement/stmt_if.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use ruff_formatter::{format_args, write};
use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::{ElifElseClause, StmtIf};
use ruff_python_ast::{AnyNodeRef, ElifElseClause, StmtIf};
use ruff_text_size::Ranged;

use crate::expression::maybe_parenthesize_expression;
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
use crate::statement::suite::SuiteKind;

#[derive(Default)]
pub struct FormatStmtIf;
Expand Down Expand Up @@ -35,13 +35,22 @@ impl FormatNodeRule<StmtIf> for FormatStmtIf {
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();
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();
}

Expand All @@ -55,6 +64,7 @@ pub(crate) fn format_elif_else_clause(
item: &ElifElseClause,
f: &mut PyFormatter,
last_node: Option<AnyNodeRef>,
suite_kind: SuiteKind,
) -> FormatResult<()> {
let ElifElseClause {
range: _,
Expand Down Expand Up @@ -93,7 +103,7 @@ pub(crate) fn format_elif_else_clause(
}),
)
.with_leading_comments(leading_comments, last_node),
clause_body(body, trailing_colon_comment),
clause_body(body, suite_kind, trailing_colon_comment),
f.options()
.source_map_generation()
.is_enabled()
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff_python_formatter/src/statement/stmt_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,15 @@ impl FormatNodeRule<StmtMatch> for FormatStmtMatch {
let mut last_case = first;

for case in cases_iter {
let last_suite_in_statement = Some(case) == cases.last();
write!(
f,
[block_indent(&format_args!(
leading_alternate_branch_comments(
comments.leading(case),
last_case.body.last(),
),
case.format()
case.format().with_options(last_suite_in_statement)
))]
)?;
last_case = case;
Expand Down
Loading

0 comments on commit 9a817a2

Please sign in to comment.