Skip to content

Commit af95cba

Browse files
authored
Add newline after module docstrings in preview style (#8283)
Change ```python """Test docstring""" a = 1 ``` to ```python """Test docstring""" a = 1 ``` in preview style, but don't touch the docstring otherwise. Do we want to ask black to also format the content of module level docstrings? Seems inconsistent to me that we change function and class docstring indentation/contents but not module docstrings. Fixes #7995
1 parent fc94857 commit af95cba

File tree

2 files changed

+28
-12
lines changed

2 files changed

+28
-12
lines changed

crates/ruff_python_formatter/src/statement/suite.rs

+27-12
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::verbatim::{
1919
};
2020

2121
/// Level at which the [`Suite`] appears in the source code.
22-
#[derive(Copy, Clone, Debug, Default)]
22+
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
2323
pub enum SuiteKind {
2424
/// Statements at the module level / top level
2525
TopLevel,
@@ -123,7 +123,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
123123

124124
let first_comments = comments.leading_dangling_trailing(first);
125125

126-
let (mut preceding, mut after_class_docstring) = if first_comments
126+
let (mut preceding, mut empty_line_after_docstring) = if first_comments
127127
.leading
128128
.iter()
129129
.any(|comment| comment.is_suppression_off_comment(source))
@@ -143,11 +143,24 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
143143
)
144144
} else {
145145
first.fmt(f)?;
146-
(
147-
first.statement(),
148-
matches!(first, SuiteChildStatement::Docstring(_))
149-
&& matches!(self.kind, SuiteKind::Class),
150-
)
146+
147+
#[allow(clippy::if_same_then_else)]
148+
let empty_line_after_docstring = if matches!(first, SuiteChildStatement::Docstring(_))
149+
&& self.kind == SuiteKind::Class
150+
{
151+
true
152+
} else if f.options().preview().is_enabled()
153+
&& self.kind == SuiteKind::TopLevel
154+
&& DocstringStmt::try_from_statement(first.statement()).is_some()
155+
{
156+
// Only in preview mode, insert a newline after a module level docstring, but treat
157+
// it as a docstring otherwise. See: https://github.com/psf/black/pull/3932.
158+
true
159+
} else {
160+
false
161+
};
162+
163+
(first.statement(), empty_line_after_docstring)
151164
};
152165

153166
let mut preceding_comments = comments.leading_dangling_trailing(preceding);
@@ -303,7 +316,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
303316
}
304317
},
305318
}
306-
} else if after_class_docstring {
319+
} else if empty_line_after_docstring {
307320
// Enforce an empty line after a class docstring, e.g., these are both stable
308321
// formatting:
309322
// ```python
@@ -389,7 +402,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
389402
preceding_comments = following_comments;
390403
}
391404

392-
after_class_docstring = false;
405+
empty_line_after_docstring = false;
393406
}
394407

395408
Ok(())
@@ -547,9 +560,11 @@ impl<'a> DocstringStmt<'a> {
547560
};
548561

549562
if let Expr::Constant(ExprConstant { value, .. }) = value.as_ref() {
550-
if !value.is_implicit_concatenated() {
551-
return Some(DocstringStmt(stmt));
552-
}
563+
return match value {
564+
Constant::Str(value) if !value.implicit_concatenated => Some(DocstringStmt(stmt)),
565+
Constant::Bytes(value) if !value.implicit_concatenated => Some(DocstringStmt(stmt)),
566+
_ => None,
567+
};
553568
}
554569

555570
None

crates/ruff_python_formatter/tests/snapshots/[email protected]

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ preview = Enabled
166166
"""
167167
Black's `Preview.module_docstring_newlines`
168168
"""
169+
169170
first_stmt_after_module_level_docstring = 1
170171
171172

0 commit comments

Comments
 (0)