Skip to content

Commit bacae0c

Browse files
committed
Re-add for functions and classes
1 parent 693ba92 commit bacae0c

11 files changed

+207
-360
lines changed

crates/ruff_python_formatter/src/comments/format.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,3 +475,45 @@ fn normalize_comment<'a>(
475475

476476
Ok(Cow::Owned(std::format!("# {}", content.trim_start())))
477477
}
478+
479+
/// Format the empty lines between a node and its trailing comments.
480+
///
481+
/// For example, given:
482+
/// ```python
483+
/// def func():
484+
/// ...
485+
/// # comment
486+
/// ```
487+
///
488+
/// This builder will insert two empty lines before the comment.
489+
/// ```
490+
pub(crate) const fn empty_lines_before_trailing_comments(
491+
comments: &[SourceComment],
492+
expected: u32,
493+
) -> FormatEmptyLinesBeforeTrailingComments {
494+
FormatEmptyLinesBeforeTrailingComments { comments, expected }
495+
}
496+
497+
#[derive(Copy, Clone, Debug)]
498+
pub(crate) struct FormatEmptyLinesBeforeTrailingComments<'a> {
499+
/// The trailing comments of the node.
500+
comments: &'a [SourceComment],
501+
/// The expected number of empty lines before the trailing comments.
502+
expected: u32,
503+
}
504+
505+
impl Format<PyFormatContext<'_>> for FormatEmptyLinesBeforeTrailingComments<'_> {
506+
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
507+
if let Some(comment) = self
508+
.comments
509+
.iter()
510+
.find(|comment| comment.line_position().is_own_line())
511+
{
512+
let actual = lines_before(comment.start(), f.context().source()).saturating_sub(1);
513+
for _ in actual..self.expected {
514+
write!(f, [empty_line()])?;
515+
}
516+
}
517+
Ok(())
518+
}
519+
}

crates/ruff_python_formatter/src/statement/stmt_class_def.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use ruff_python_ast::{Decorator, StmtClassDef};
33
use ruff_python_trivia::lines_after_ignoring_trivia;
44
use ruff_text_size::Ranged;
55

6+
use crate::comments::format::empty_lines_before_trailing_comments;
67
use crate::comments::{leading_comments, trailing_comments, SourceComment};
8+
use crate::context::NodeLevel;
79
use crate::prelude::*;
810
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
911
use crate::statement::suite::SuiteKind;
@@ -108,7 +110,33 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
108110
),
109111
clause_body(body, trailing_definition_comments).with_kind(SuiteKind::Class),
110112
]
113+
)?;
114+
115+
// If the class contains trailing comments, insert newlines before them.
116+
// For example, given:
117+
// ```python
118+
// class Class:
119+
// ...
120+
// # comment
121+
// ```
122+
//
123+
// At the top-level, reformat as:
124+
// ```python
125+
// class Class:
126+
// ...
127+
//
128+
//
129+
// # comment
130+
// ```
131+
empty_lines_before_trailing_comments(
132+
comments.trailing(item),
133+
if f.context().node_level() == NodeLevel::TopLevel {
134+
2
135+
} else {
136+
1
137+
},
111138
)
139+
.fmt(f)
112140
}
113141

114142
fn fmt_dangling_comments(

crates/ruff_python_formatter/src/statement/stmt_function_def.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use crate::comments::format::empty_lines_before_trailing_comments;
12
use ruff_formatter::write;
23
use ruff_python_ast::{Parameters, StmtFunctionDef};
34
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
45
use ruff_text_size::Ranged;
56

67
use crate::comments::SourceComment;
8+
use crate::context::NodeLevel;
79
use crate::expression::maybe_parenthesize_expression;
810
use crate::expression::parentheses::{Parentheses, Parenthesize};
911
use crate::prelude::*;
@@ -144,7 +146,33 @@ impl FormatNodeRule<StmtFunctionDef> for FormatStmtFunctionDef {
144146
),
145147
clause_body(body, trailing_definition_comments).with_kind(SuiteKind::Function),
146148
]
149+
)?;
150+
151+
// If the function contains trailing comments, insert newlines before them.
152+
// For example, given:
153+
// ```python
154+
// def func():
155+
// ...
156+
// # comment
157+
// ```
158+
//
159+
// At the top-level, reformat as:
160+
// ```python
161+
// def func():
162+
// ...
163+
//
164+
//
165+
// # comment
166+
// ```
167+
empty_lines_before_trailing_comments(
168+
comments.trailing(item),
169+
if f.context().node_level() == NodeLevel::TopLevel {
170+
2
171+
} else {
172+
1
173+
},
147174
)
175+
.fmt(f)
148176
}
149177

150178
fn fmt_dangling_comments(

crates/ruff_python_formatter/src/statement/suite.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,12 +195,13 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
195195
empty_line().fmt(f)?;
196196
}
197197
}
198-
} else if (is_import_definition(preceding)
199-
&& !comments.has_trailing_own_line(preceding))
198+
} else if is_import_definition(preceding)
200199
&& (!is_import_definition(following) || comments.has_leading(following))
201200
{
202201
// Enforce _at least_ one empty line after an import statement (but allow up to
203-
// two at the top-level).
202+
// two at the top-level). In this context, "after an import statement" means that
203+
// that the previous node is an import, and the following node is an import _or_ has
204+
// a leading comment.
204205
match self.kind {
205206
SuiteKind::TopLevel => {
206207
match lines_after_ignoring_trivia(preceding.end(), source) {

0 commit comments

Comments
 (0)