From 4ff458a37b4f781b041f2bc3076fbc0fabe7dcb1 Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 25 Oct 2023 15:40:37 +0200 Subject: [PATCH 1/2] Insert newline between docstring and following own line comment --- .../resources/test/fixtures/ruff/docstring.py | 39 ++++ .../src/comments/placement.rs | 9 +- .../src/statement/suite.rs | 26 ++- ...mpatibility@simple_cases__fmtonoff.py.snap | 10 +- .../tests/snapshots/format@docstring.py.snap | 199 ++++++++++++++++++ 5 files changed, 276 insertions(+), 7 deletions(-) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring.py index 17edab372513d..f419cb9b047eb 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring.py @@ -97,6 +97,45 @@ class ByteDocstring: b""" has leading whitespace""" first_statement = 1 +class CommentAfterDocstring1: + """Browse module classes and functions in IDLE.""" + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring2: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring3: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring4: + """Browse module classes and functions in IDLE.""" + + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring5: + """Browse module classes and functions in IDLE.""" + # This class is also the base class for pathbrowser.PathBrowser. + + class TabbedIndent: def tabbed_indent(self): """check for correct tabbed formatting diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index 3b6bde34f666c..2858819c2e140 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -1,8 +1,9 @@ use std::cmp::Ordering; use ruff_python_ast::whitespace::indentation; -use ruff_python_ast::AnyNodeRef; -use ruff_python_ast::{self as ast, Comprehension, Expr, MatchCase, ModModule, Parameters}; +use ruff_python_ast::{ + self as ast, AnyNodeRef, Comprehension, Expr, MatchCase, ModModule, Parameters, +}; use ruff_python_trivia::{ find_only_token_in_range, indentation_at_offset, BackwardsTokenizer, CommentRanges, SimpleToken, SimpleTokenKind, SimpleTokenizer, @@ -544,6 +545,10 @@ fn handle_own_line_comment_between_statements<'a>( return CommentPlacement::Default(comment); } + if comment.line_position().is_end_of_line() { + return CommentPlacement::Default(comment); + } + // If the comment is directly attached to the following statement; make it a leading // comment: // ```python diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index a32184f5b7da3..16dc8252e71ea 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -584,9 +584,31 @@ impl Format> for DocstringStmt<'_> { string_literal .format() .with_options(StringLayout::DocString), - trailing_comments(node_comments.trailing), ] - ) + )?; + + // Comments after docstrings need a newline between the docstring and the comment, so we attach + // it as leading on the first statement after the docstring. + // (https://github.com/astral-sh/ruff/issues/7948) + // ```python + // class ModuleBrowser: + // """Browse module classes and functions in IDLE.""" + // # ^ Insert a newline above here + // + // def __init__(self, master, path, *, _htest=False, _utest=False): + // pass + // ``` + if let Some(own_line) = node_comments + .trailing + .iter() + .find(|comment| comment.line_position().is_own_line()) + { + if lines_before(own_line.start(), f.context().source()) < 2 { + empty_line().fmt(f)?; + } + } + + trailing_comments(node_comments.trailing).fmt(f) } } } diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap index ce417c728b56b..ef41670ec0231 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap @@ -225,8 +225,11 @@ d={'a':1, # fmt: on goes + here, andhere, -@@ -122,8 +123,10 @@ +@@ -120,10 +121,13 @@ + + The comments between will be formatted. This is a known limitation. """ ++ # fmt: off - # hey, that won't work @@ -237,7 +240,7 @@ d={'a':1, # fmt: on pass -@@ -138,7 +141,7 @@ +@@ -138,7 +142,7 @@ now . considers . multiple . fmt . directives . within . one . prefix # fmt: on # fmt: off @@ -246,7 +249,7 @@ d={'a':1, # fmt: on -@@ -178,14 +181,18 @@ +@@ -178,14 +182,18 @@ $ """, # fmt: off @@ -395,6 +398,7 @@ def off_and_on_without_data(): The comments between will be formatted. This is a known limitation. """ + # fmt: off diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap index 580b55562af07..134dee817aa37 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap @@ -103,6 +103,45 @@ class ByteDocstring: b""" has leading whitespace""" first_statement = 1 +class CommentAfterDocstring1: + """Browse module classes and functions in IDLE.""" + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring2: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring3: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring4: + """Browse module classes and functions in IDLE.""" + + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring5: + """Browse module classes and functions in IDLE.""" + # This class is also the base class for pathbrowser.PathBrowser. + + class TabbedIndent: def tabbed_indent(self): """check for correct tabbed formatting @@ -222,6 +261,46 @@ class ByteDocstring: first_statement = 1 +class CommentAfterDocstring1: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring2: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring3: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring4: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring5: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + class TabbedIndent: def tabbed_indent(self): """check for correct tabbed formatting @@ -341,6 +420,46 @@ class ByteDocstring: first_statement = 1 +class CommentAfterDocstring1: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring2: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring3: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring4: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring5: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + class TabbedIndent: def tabbed_indent(self): """check for correct tabbed formatting @@ -460,6 +579,46 @@ class ByteDocstring: first_statement = 1 +class CommentAfterDocstring1: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring2: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring3: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring4: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring5: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + class TabbedIndent: def tabbed_indent(self): """check for correct tabbed formatting @@ -579,6 +738,46 @@ class ByteDocstring: first_statement = 1 +class CommentAfterDocstring1: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring2: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + def __init__(self): + pass + + +class CommentAfterDocstring3: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring4: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + def __init__(self): + pass + + +class CommentAfterDocstring5: + """Browse module classes and functions in IDLE.""" + + # This class is also the base class for pathbrowser.PathBrowser. + + class TabbedIndent: def tabbed_indent(self): """check for correct tabbed formatting From 7a35a5a8d852b534f89c844e61d151265dafbf2e Mon Sep 17 00:00:00 2001 From: konstin Date: Mon, 30 Oct 2023 14:08:58 +0100 Subject: [PATCH 2/2] Update stale comment --- crates/ruff_python_formatter/src/statement/suite.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index 16dc8252e71ea..a78f692d5c985 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -587,8 +587,7 @@ impl Format> for DocstringStmt<'_> { ] )?; - // Comments after docstrings need a newline between the docstring and the comment, so we attach - // it as leading on the first statement after the docstring. + // Comments after docstrings need a newline between the docstring and the comment. // (https://github.com/astral-sh/ruff/issues/7948) // ```python // class ModuleBrowser: