mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 02:12:22 +00:00
No newline after function docstrings (#8375)
Fixup for #8216 to not apply to function docstrings. Main before #8216: | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75804 | 1799 | 1648 | | django | 0.99984 | 2772 | 33 | | home-assistant | 0.99963 | 10596 | 148 | | poetry | 0.99925 | 317 | 12 | | transformers | 0.99967 | 2657 | 328 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99980 | 3669 | 18 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 22 | main now: | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75804 | 1799 | 1648 | | django | 0.99984 | 2772 | 48 | | home-assistant | 0.99963 | 10596 | 181 | | poetry | 0.99925 | 317 | 12 | | transformers | 0.99967 | 2657 | 339 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99980 | 3669 | 18 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 23 | PR: | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75804 | 1799 | 1648 | | django | 0.99984 | 2772 | 33 | | home-assistant | 0.99963 | 10596 | 148 | | poetry | 0.99925 | 317 | 12 | | transformers | 0.99967 | 2657 | 328 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99980 | 3669 | 18 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 22 |
This commit is contained in:
parent
23ed4e9616
commit
3076d76b0a
4 changed files with 82 additions and 34 deletions
|
@ -136,6 +136,13 @@ class CommentAfterDocstring5:
|
|||
# This class is also the base class for pathbrowser.PathBrowser.
|
||||
|
||||
|
||||
def f():
|
||||
"""Browse module classes and functions in IDLE."""
|
||||
# ^ Do not insert a newline above here
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
|
|
|
@ -89,7 +89,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
|||
}
|
||||
|
||||
SuiteKind::Function => {
|
||||
if let Some(docstring) = DocstringStmt::try_from_statement(first) {
|
||||
if let Some(docstring) = DocstringStmt::try_from_statement(first, self.kind) {
|
||||
SuiteChildStatement::Docstring(docstring)
|
||||
} else {
|
||||
SuiteChildStatement::Other(first)
|
||||
|
@ -97,7 +97,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
|||
}
|
||||
|
||||
SuiteKind::Class => {
|
||||
if let Some(docstring) = DocstringStmt::try_from_statement(first) {
|
||||
if let Some(docstring) = DocstringStmt::try_from_statement(first, self.kind) {
|
||||
if !comments.has_leading(first)
|
||||
&& lines_before(first.start(), source) > 1
|
||||
&& !source_type.is_stub()
|
||||
|
@ -150,7 +150,7 @@ impl FormatRule<Suite, PyFormatContext<'_>> for FormatSuite {
|
|||
true
|
||||
} else if f.options().preview().is_enabled()
|
||||
&& self.kind == SuiteKind::TopLevel
|
||||
&& DocstringStmt::try_from_statement(first.statement()).is_some()
|
||||
&& DocstringStmt::try_from_statement(first.statement(), self.kind).is_some()
|
||||
{
|
||||
// Only in preview mode, insert a newline after a module level docstring, but treat
|
||||
// it as a docstring otherwise. See: https://github.com/psf/black/pull/3932.
|
||||
|
@ -543,17 +543,25 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for Suite {
|
|||
|
||||
/// A statement representing a docstring.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct DocstringStmt<'a>(&'a Stmt);
|
||||
pub(crate) struct DocstringStmt<'a> {
|
||||
/// The [`Stmt::Expr`]
|
||||
docstring: &'a Stmt,
|
||||
/// The parent suite kind
|
||||
suite_kind: SuiteKind,
|
||||
}
|
||||
|
||||
impl<'a> DocstringStmt<'a> {
|
||||
/// Checks if the statement is a simple string that can be formatted as a docstring
|
||||
fn try_from_statement(stmt: &'a Stmt) -> Option<DocstringStmt<'a>> {
|
||||
fn try_from_statement(stmt: &'a Stmt, suite_kind: SuiteKind) -> Option<DocstringStmt<'a>> {
|
||||
let Stmt::Expr(ast::StmtExpr { value, .. }) = stmt else {
|
||||
return None;
|
||||
};
|
||||
|
||||
match value.as_ref() {
|
||||
Expr::StringLiteral(value) if !value.implicit_concatenated => Some(DocstringStmt(stmt)),
|
||||
Expr::StringLiteral(value) if !value.implicit_concatenated => Some(DocstringStmt {
|
||||
docstring: stmt,
|
||||
suite_kind,
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -562,14 +570,14 @@ impl<'a> DocstringStmt<'a> {
|
|||
impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||
let comments = f.context().comments().clone();
|
||||
let node_comments = comments.leading_dangling_trailing(self.0);
|
||||
let node_comments = comments.leading_dangling_trailing(self.docstring);
|
||||
|
||||
if FormatStmtExpr.is_suppressed(node_comments.trailing, f.context()) {
|
||||
suppressed_node(self.0).fmt(f)
|
||||
suppressed_node(self.docstring).fmt(f)
|
||||
} else {
|
||||
// SAFETY: Safe because `DocStringStmt` guarantees that it only ever wraps a `ExprStmt` containing a `ExprStringLiteral`.
|
||||
let string_literal = self
|
||||
.0
|
||||
.docstring
|
||||
.as_expr_stmt()
|
||||
.unwrap()
|
||||
.value
|
||||
|
@ -587,8 +595,9 @@ impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
|||
]
|
||||
)?;
|
||||
|
||||
// Comments after docstrings need a newline between the docstring and the comment.
|
||||
// (https://github.com/astral-sh/ruff/issues/7948)
|
||||
if self.suite_kind == SuiteKind::Class {
|
||||
// Comments after class docstrings need a newline between the docstring and the
|
||||
// comment (https://github.com/astral-sh/ruff/issues/7948).
|
||||
// ```python
|
||||
// class ModuleBrowser:
|
||||
// """Browse module classes and functions in IDLE."""
|
||||
|
@ -606,6 +615,7 @@ impl Format<PyFormatContext<'_>> for DocstringStmt<'_> {
|
|||
empty_line().fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trailing_comments(node_comments.trailing).fmt(f)
|
||||
}
|
||||
|
@ -625,7 +635,7 @@ pub(crate) enum SuiteChildStatement<'a> {
|
|||
impl<'a> SuiteChildStatement<'a> {
|
||||
pub(crate) const fn statement(self) -> &'a Stmt {
|
||||
match self {
|
||||
SuiteChildStatement::Docstring(docstring) => docstring.0,
|
||||
SuiteChildStatement::Docstring(docstring) => docstring.docstring,
|
||||
SuiteChildStatement::Other(statement) => statement,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,11 +225,8 @@ d={'a':1,
|
|||
# fmt: on
|
||||
goes + here,
|
||||
andhere,
|
||||
@@ -120,10 +121,13 @@
|
||||
|
||||
The comments between will be formatted. This is a known limitation.
|
||||
@@ -122,8 +123,10 @@
|
||||
"""
|
||||
+
|
||||
# fmt: off
|
||||
|
||||
- # hey, that won't work
|
||||
|
@ -240,7 +237,7 @@ d={'a':1,
|
|||
# fmt: on
|
||||
pass
|
||||
|
||||
@@ -138,7 +142,7 @@
|
||||
@@ -138,7 +141,7 @@
|
||||
now . considers . multiple . fmt . directives . within . one . prefix
|
||||
# fmt: on
|
||||
# fmt: off
|
||||
|
@ -249,7 +246,7 @@ d={'a':1,
|
|||
# fmt: on
|
||||
|
||||
|
||||
@@ -178,14 +182,18 @@
|
||||
@@ -178,14 +181,18 @@
|
||||
$
|
||||
""",
|
||||
# fmt: off
|
||||
|
@ -398,7 +395,6 @@ def off_and_on_without_data():
|
|||
|
||||
The comments between will be formatted. This is a known limitation.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
|
||||
|
||||
|
|
|
@ -142,6 +142,13 @@ class CommentAfterDocstring5:
|
|||
# This class is also the base class for pathbrowser.PathBrowser.
|
||||
|
||||
|
||||
def f():
|
||||
"""Browse module classes and functions in IDLE."""
|
||||
# ^ Do not insert a newline above here
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
|
@ -301,6 +308,13 @@ class CommentAfterDocstring5:
|
|||
# This class is also the base class for pathbrowser.PathBrowser.
|
||||
|
||||
|
||||
def f():
|
||||
"""Browse module classes and functions in IDLE."""
|
||||
# ^ Do not insert a newline above here
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
|
@ -460,6 +474,13 @@ class CommentAfterDocstring5:
|
|||
# This class is also the base class for pathbrowser.PathBrowser.
|
||||
|
||||
|
||||
def f():
|
||||
"""Browse module classes and functions in IDLE."""
|
||||
# ^ Do not insert a newline above here
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
|
@ -619,6 +640,13 @@ class CommentAfterDocstring5:
|
|||
# This class is also the base class for pathbrowser.PathBrowser.
|
||||
|
||||
|
||||
def f():
|
||||
"""Browse module classes and functions in IDLE."""
|
||||
# ^ Do not insert a newline above here
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
|
@ -778,6 +806,13 @@ class CommentAfterDocstring5:
|
|||
# This class is also the base class for pathbrowser.PathBrowser.
|
||||
|
||||
|
||||
def f():
|
||||
"""Browse module classes and functions in IDLE."""
|
||||
# ^ Do not insert a newline above here
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue