diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/decorators.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/decorators.py new file mode 100644 index 0000000000000..98e3bedc6b94f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/decorators.py @@ -0,0 +1,19 @@ + +@FormattedDecorator(a =b) + # leading comment +@MyDecorator( # dangling comment + list = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12], x = some_other_function_call({ "test": "value", "more": "other"})) # fmt: skip + # leading class comment +class Test: + pass + + + +@FormattedDecorator(a =b) +# leading comment +@MyDecorator( # dangling comment + list = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12], x = some_other_function_call({ "test": "value", "more": "other"})) # fmt: skip +# leading class comment +def test(): + pass + diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/docstrings.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/docstrings.py new file mode 100644 index 0000000000000..2f8bdcef3bd0b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/docstrings.py @@ -0,0 +1,13 @@ +def test(): + # leading comment + """ This docstring does not + get formatted + """ # fmt: skip + # trailing comment + +def test(): + # leading comment + """ This docstring gets formatted + """ # trailing comment + + and_this + gets + formatted + too diff --git a/crates/ruff_python_formatter/src/comments/mod.rs b/crates/ruff_python_formatter/src/comments/mod.rs index 38d38fdf07f84..04d4c5da7bd48 100644 --- a/crates/ruff_python_formatter/src/comments/mod.rs +++ b/crates/ruff_python_formatter/src/comments/mod.rs @@ -215,6 +215,18 @@ pub(crate) enum SuppressionKind { Skip, } +impl SuppressionKind { + pub(crate) fn has_skip_comment(trailing_comments: &[SourceComment], source: &str) -> bool { + trailing_comments.iter().any(|comment| { + comment.line_position().is_end_of_line() + && matches!( + comment.suppression_kind(source), + Some(SuppressionKind::Skip | SuppressionKind::Off) + ) + }) + } +} + impl Ranged for SourceComment { #[inline] fn range(&self) -> TextRange { diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index b36c40c9f412b..d0d6d71524ebb 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -905,7 +905,7 @@ fn handle_leading_class_with_decorators_comment<'a>( comment: DecoratedComment<'a>, class_def: &'a ast::StmtClassDef, ) -> CommentPlacement<'a> { - if comment.start() < class_def.name.start() { + if comment.line_position().is_own_line() && comment.start() < class_def.name.start() { if let Some(decorator) = class_def.decorator_list.last() { if decorator.end() < comment.start() { return CommentPlacement::dangling(class_def, comment); diff --git a/crates/ruff_python_formatter/src/expression/mod.rs b/crates/ruff_python_formatter/src/expression/mod.rs index a617c91328776..1a7a6b4a05baa 100644 --- a/crates/ruff_python_formatter/src/expression/mod.rs +++ b/crates/ruff_python_formatter/src/expression/mod.rs @@ -94,7 +94,7 @@ impl FormatRule> for FormatExpr { Expr::List(expr) => expr.format().fmt(f), Expr::Tuple(expr) => expr.format().fmt(f), Expr::Slice(expr) => expr.format().fmt(f), - Expr::IpyEscapeCommand(_) => todo!(), + Expr::IpyEscapeCommand(expr) => expr.format().fmt(f), }); let parenthesize = match parentheses { diff --git a/crates/ruff_python_formatter/src/lib.rs b/crates/ruff_python_formatter/src/lib.rs index d088569905842..ebbe758e8f8c4 100644 --- a/crates/ruff_python_formatter/src/lib.rs +++ b/crates/ruff_python_formatter/src/lib.rs @@ -19,6 +19,7 @@ use crate::comments::{ }; use crate::context::PyFormatContext; pub use crate::options::{MagicTrailingComma, PyFormatOptions, QuoteStyle}; +use crate::verbatim::suppressed_node; pub(crate) mod builders; pub mod cli; @@ -50,24 +51,21 @@ where let node_comments = comments.leading_dangling_trailing_comments(node.as_any_node_ref()); - write!( - f, - [ - leading_comments(node_comments.leading), - source_position(node.start()) - ] - )?; - - self.fmt_fields(node, f)?; - self.fmt_dangling_comments(node_comments.dangling, f)?; - - write!( - f, - [ - source_position(node.end()), - trailing_comments(node_comments.trailing) - ] - ) + if self.is_suppressed(node_comments.trailing, f.context()) { + suppressed_node(node.as_any_node_ref()).fmt(f) + } else { + leading_comments(node_comments.leading).fmt(f)?; + self.fmt_fields(node, f)?; + self.fmt_dangling_comments(node_comments.dangling, f)?; + + write!( + f, + [ + source_position(node.end()), + trailing_comments(node_comments.trailing) + ] + ) + } } /// Formats the node's fields. @@ -87,6 +85,14 @@ where ) -> FormatResult<()> { dangling_comments(dangling_node_comments).fmt(f) } + + fn is_suppressed( + &self, + _trailing_comments: &[SourceComment], + _context: &PyFormatContext, + ) -> bool { + false + } } #[derive(Error, Debug)] @@ -236,16 +242,12 @@ if True: #[ignore] #[test] fn quick_test() { - let src = r#"def test(): - # fmt: off - - a + b - - - - # suppressed comments + let src = r#" +@MyDecorator(list = a) # fmt: skip +# trailing comment +class Test: + pass -a + b # formatted "#; // Tokenize once diff --git a/crates/ruff_python_formatter/src/other/decorator.rs b/crates/ruff_python_formatter/src/other/decorator.rs index 9e6ef4219a612..5ff346fa0fc54 100644 --- a/crates/ruff_python_formatter/src/other/decorator.rs +++ b/crates/ruff_python_formatter/src/other/decorator.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; @@ -23,4 +24,12 @@ impl FormatNodeRule for FormatDecorator { ] ) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/mod.rs b/crates/ruff_python_formatter/src/statement/mod.rs index cf39929eb9598..2eed3ce744385 100644 --- a/crates/ruff_python_formatter/src/statement/mod.rs +++ b/crates/ruff_python_formatter/src/statement/mod.rs @@ -1,7 +1,8 @@ -use crate::prelude::*; use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule}; use ruff_python_ast::Stmt; +use crate::prelude::*; + pub(crate) mod stmt_ann_assign; pub(crate) mod stmt_assert; pub(crate) mod stmt_assign; @@ -59,7 +60,7 @@ impl FormatRule> for FormatStmt { Stmt::Break(x) => x.format().fmt(f), Stmt::Continue(x) => x.format().fmt(f), Stmt::TypeAlias(x) => x.format().fmt(f), - Stmt::IpyEscapeCommand(_) => todo!(), + Stmt::IpyEscapeCommand(x) => x.format().fmt(f), } } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_ann_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_ann_assign.rs index e4bb2649d8115..ebfb1e0f78588 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_ann_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_ann_assign.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; @@ -42,4 +43,12 @@ impl FormatNodeRule for FormatStmtAnnAssign { Ok(()) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_assert.rs b/crates/ruff_python_formatter/src/statement/stmt_assert.rs index c6dae63a178e6..eb5a859a25501 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_assert.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_assert.rs @@ -1,3 +1,5 @@ +use crate::comments::{SourceComment, SuppressionKind}; +use crate::context::PyFormatContext; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::{FormatNodeRule, PyFormatter}; @@ -38,4 +40,12 @@ impl FormatNodeRule for FormatStmtAssert { Ok(()) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_assign.rs index d54804939dc9a..49759a7ee4796 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_assign.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use ruff_formatter::{format_args, write, FormatError}; use ruff_python_ast::{Expr, StmtAssign}; @@ -42,6 +43,14 @@ impl FormatNodeRule for FormatStmtAssign { )] ) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } #[derive(Debug)] diff --git a/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs b/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs index 6123935e49ad6..652247db46606 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_aug_assign.rs @@ -1,9 +1,11 @@ +use ruff_formatter::write; +use ruff_python_ast::StmtAugAssign; + +use crate::comments::{SourceComment, SuppressionKind}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::{AsFormat, FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{space, text}; -use ruff_formatter::{write, Buffer, FormatResult}; -use ruff_python_ast::StmtAugAssign; +use crate::prelude::*; +use crate::{AsFormat, FormatNodeRule}; #[derive(Default)] pub struct FormatStmtAugAssign; @@ -28,4 +30,12 @@ impl FormatNodeRule for FormatStmtAugAssign { ] ) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_break.rs b/crates/ruff_python_formatter/src/statement/stmt_break.rs index 3024faae9fd19..b35a884043514 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_break.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_break.rs @@ -1,8 +1,9 @@ -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::text; -use ruff_formatter::{Format, FormatResult}; use ruff_python_ast::StmtBreak; +use crate::comments::{SourceComment, SuppressionKind}; +use crate::prelude::*; +use crate::FormatNodeRule; + #[derive(Default)] pub struct FormatStmtBreak; @@ -10,4 +11,12 @@ impl FormatNodeRule for FormatStmtBreak { fn fmt_fields(&self, _item: &StmtBreak, f: &mut PyFormatter) -> FormatResult<()> { text("break").fmt(f) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_class_def.rs b/crates/ruff_python_formatter/src/statement/stmt_class_def.rs index 9a908b95935d4..3b4a1f2ac0165 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_class_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_class_def.rs @@ -1,5 +1,5 @@ -use ruff_formatter::{write, Buffer}; -use ruff_python_ast::{Ranged, StmtClassDef}; +use ruff_formatter::write; +use ruff_python_ast::{Decorator, Ranged, StmtClassDef}; use ruff_python_trivia::lines_after_ignoring_trivia; use crate::comments::{leading_comments, trailing_comments, SourceComment}; @@ -30,31 +30,11 @@ impl FormatNodeRule for FormatStmtClassDef { let (leading_definition_comments, trailing_definition_comments) = dangling_comments.split_at(trailing_definition_comments_start); - if let Some(last_decorator) = decorator_list.last() { - f.join_with(hard_line_break()) - .entries(decorator_list.iter().formatted()) - .finish()?; - - if leading_definition_comments.is_empty() { - write!(f, [hard_line_break()])?; - } else { - // Write any leading definition comments (between last decorator and the header) - // while maintaining the right amount of empty lines between the comment - // and the last decorator. - let leading_line = - if lines_after_ignoring_trivia(last_decorator.end(), f.context().source()) <= 1 - { - hard_line_break() - } else { - empty_line() - }; - - write!( - f, - [leading_line, leading_comments(leading_definition_comments)] - )?; - } + FormatDecorators { + decorators: decorator_list, + leading_definition_comments, } + .fmt(f)?; write!(f, [text("class"), space(), name.format()])?; @@ -136,3 +116,43 @@ impl FormatNodeRule for FormatStmtClassDef { Ok(()) } } + +pub(super) struct FormatDecorators<'a> { + pub(super) decorators: &'a [Decorator], + pub(super) leading_definition_comments: &'a [SourceComment], +} + +impl Format> for FormatDecorators<'_> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + if let Some(last_decorator) = self.decorators.last() { + f.join_with(hard_line_break()) + .entries(self.decorators.iter().formatted()) + .finish()?; + + if self.leading_definition_comments.is_empty() { + write!(f, [hard_line_break()])?; + } else { + // Write any leading definition comments (between last decorator and the header) + // while maintaining the right amount of empty lines between the comment + // and the last decorator. + let leading_line = + if lines_after_ignoring_trivia(last_decorator.end(), f.context().source()) <= 1 + { + hard_line_break() + } else { + empty_line() + }; + + write!( + f, + [ + leading_line, + leading_comments(self.leading_definition_comments) + ] + )?; + } + } + + Ok(()) + } +} diff --git a/crates/ruff_python_formatter/src/statement/stmt_continue.rs b/crates/ruff_python_formatter/src/statement/stmt_continue.rs index 8b2ab90e0e663..88c43877d8e57 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_continue.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_continue.rs @@ -1,8 +1,9 @@ -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::text; -use ruff_formatter::{Format, FormatResult}; +use crate::comments::{SourceComment, SuppressionKind}; use ruff_python_ast::StmtContinue; +use crate::prelude::*; +use crate::FormatNodeRule; + #[derive(Default)] pub struct FormatStmtContinue; @@ -10,4 +11,12 @@ impl FormatNodeRule for FormatStmtContinue { fn fmt_fields(&self, _item: &StmtContinue, f: &mut PyFormatter) -> FormatResult<()> { text("continue").fmt(f) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_delete.rs b/crates/ruff_python_formatter/src/statement/stmt_delete.rs index 77592c732e618..9cb0e367fd71f 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_delete.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_delete.rs @@ -1,11 +1,12 @@ +use ruff_formatter::write; +use ruff_python_ast::{Ranged, StmtDelete}; + use crate::builders::{parenthesize_if_expands, PyFormatterExtensions}; -use crate::comments::{dangling_node_comments, SourceComment}; +use crate::comments::{dangling_node_comments, SourceComment, SuppressionKind}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{block_indent, format_with, space, text}; -use ruff_formatter::{write, Buffer, Format, FormatResult}; -use ruff_python_ast::{Ranged, StmtDelete}; +use crate::prelude::*; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatStmtDelete; @@ -61,4 +62,12 @@ impl FormatNodeRule for FormatStmtDelete { // Handled in `fmt_fields` Ok(()) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_expr.rs b/crates/ruff_python_formatter/src/statement/stmt_expr.rs index 0a3365e2b999a..ff30886fb0db4 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_expr.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_expr.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use ruff_python_ast as ast; use ruff_python_ast::{Expr, Operator, StmtExpr}; @@ -19,6 +20,14 @@ impl FormatNodeRule for FormatStmtExpr { value.format().fmt(f) } } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } const fn is_arithmetic_like(expression: &Expr) -> bool { diff --git a/crates/ruff_python_formatter/src/statement/stmt_for.rs b/crates/ruff_python_formatter/src/statement/stmt_for.rs index bc2bbf3a67361..22bc0017cf0ee 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_for.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_for.rs @@ -1,4 +1,4 @@ -use ruff_formatter::{format_args, write, Buffer, FormatResult}; +use ruff_formatter::{format_args, write}; use ruff_python_ast::{Expr, Ranged, Stmt, StmtFor}; use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment}; @@ -6,7 +6,7 @@ use crate::expression::expr_tuple::TupleParentheses; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; -use crate::{FormatNodeRule, PyFormatter}; +use crate::FormatNodeRule; #[derive(Debug)] struct ExprTupleWithoutParentheses<'a>(&'a Expr); diff --git a/crates/ruff_python_formatter/src/statement/stmt_function_def.rs b/crates/ruff_python_formatter/src/statement/stmt_function_def.rs index edbfb59879c1f..5897c4cab088e 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_function_def.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_function_def.rs @@ -1,11 +1,12 @@ use ruff_formatter::write; use ruff_python_ast::{Parameters, Ranged, StmtFunctionDef}; -use ruff_python_trivia::{lines_after_ignoring_trivia, SimpleTokenKind, SimpleTokenizer}; +use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; -use crate::comments::{leading_comments, trailing_comments, SourceComment}; +use crate::comments::{trailing_comments, SourceComment}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::{Parentheses, Parenthesize}; use crate::prelude::*; +use crate::statement::stmt_class_def::FormatDecorators; use crate::statement::suite::SuiteKind; use crate::FormatNodeRule; @@ -14,6 +15,17 @@ pub struct FormatStmtFunctionDef; impl FormatNodeRule for FormatStmtFunctionDef { fn fmt_fields(&self, item: &StmtFunctionDef, f: &mut PyFormatter) -> FormatResult<()> { + let StmtFunctionDef { + range: _, + is_async, + decorator_list, + name, + type_params, + parameters, + returns, + body, + } = item; + let comments = f.context().comments().clone(); let dangling_comments = comments.dangling_comments(item); @@ -23,46 +35,26 @@ impl FormatNodeRule for FormatStmtFunctionDef { let (leading_definition_comments, trailing_definition_comments) = dangling_comments.split_at(trailing_definition_comments_start); - if let Some(last_decorator) = item.decorator_list.last() { - f.join_with(hard_line_break()) - .entries(item.decorator_list.iter().formatted()) - .finish()?; - - if leading_definition_comments.is_empty() { - write!(f, [hard_line_break()])?; - } else { - // Write any leading definition comments (between last decorator and the header) - // while maintaining the right amount of empty lines between the comment - // and the last decorator. - let leading_line = - if lines_after_ignoring_trivia(last_decorator.end(), f.context().source()) <= 1 - { - hard_line_break() - } else { - empty_line() - }; - - write!( - f, - [leading_line, leading_comments(leading_definition_comments)] - )?; - } + FormatDecorators { + decorators: decorator_list, + leading_definition_comments, } + .fmt(f)?; - if item.is_async { + if *is_async { write!(f, [text("async"), space()])?; } - write!(f, [text("def"), space(), item.name.format()])?; + write!(f, [text("def"), space(), name.format()])?; - if let Some(type_params) = item.type_params.as_ref() { + if let Some(type_params) = type_params.as_ref() { write!(f, [type_params.format()])?; } let format_inner = format_with(|f: &mut PyFormatter| { - write!(f, [item.parameters.format()])?; + write!(f, [parameters.format()])?; - if let Some(return_annotation) = item.returns.as_ref() { + if let Some(return_annotation) = returns.as_ref() { write!(f, [space(), text("->"), space()])?; if return_annotation.is_tuple_expr() { @@ -110,7 +102,7 @@ impl FormatNodeRule for FormatStmtFunctionDef { [maybe_parenthesize_expression( return_annotation, item, - if empty_parameters(&item.parameters, f.context().source()) { + if empty_parameters(parameters, f.context().source()) { // If the parameters are empty, add parentheses if the return annotation // breaks at all. Parenthesize::IfBreaksOrIfRequired @@ -142,7 +134,7 @@ impl FormatNodeRule for FormatStmtFunctionDef { [ text(":"), trailing_comments(trailing_definition_comments), - block_indent(&item.body.format().with_options(SuiteKind::Function)) + block_indent(&body.format().with_options(SuiteKind::Function)) ] ) } diff --git a/crates/ruff_python_formatter/src/statement/stmt_global.rs b/crates/ruff_python_formatter/src/statement/stmt_global.rs index e3dd6bca4103d..56fbeab1ac6af 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_global.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_global.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use ruff_formatter::{format_args, write}; use ruff_python_ast::node::AstNode; use ruff_python_ast::StmtGlobal; @@ -50,4 +51,12 @@ impl FormatNodeRule for FormatStmtGlobal { ) } } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_import.rs b/crates/ruff_python_formatter/src/statement/stmt_import.rs index ea781db62533a..a8cdba2d08195 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_import.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_import.rs @@ -1,8 +1,10 @@ -use crate::{FormatNodeRule, FormattedIterExt, PyFormatter}; -use ruff_formatter::prelude::{format_args, format_with, space, text}; -use ruff_formatter::{write, Buffer, FormatResult}; +use crate::prelude::*; +use ruff_formatter::{format_args, write}; use ruff_python_ast::StmtImport; +use crate::comments::{SourceComment, SuppressionKind}; +use crate::FormatNodeRule; + #[derive(Default)] pub struct FormatStmtImport; @@ -16,4 +18,12 @@ impl FormatNodeRule for FormatStmtImport { }); write!(f, [text("import"), space(), names]) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_import_from.rs b/crates/ruff_python_formatter/src/statement/stmt_import_from.rs index ab7ffd9b9a58e..a53a73611c35d 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_import_from.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_import_from.rs @@ -1,12 +1,12 @@ -use ruff_formatter::prelude::{dynamic_text, format_with, space, text}; -use ruff_formatter::{write, Buffer, Format, FormatResult}; +use ruff_formatter::write; use ruff_python_ast::node::AstNode; use ruff_python_ast::{Ranged, StmtImportFrom}; use crate::builders::{parenthesize_if_expands, PyFormatterExtensions, TrailingComma}; -use crate::comments::SourceComment; +use crate::comments::{SourceComment, SuppressionKind}; use crate::expression::parentheses::parenthesized; -use crate::{AsFormat, FormatNodeRule, PyFormatter}; +use crate::prelude::*; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatStmtImportFrom; @@ -78,4 +78,12 @@ impl FormatNodeRule for FormatStmtImportFrom { // Handled in `fmt_fields` Ok(()) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_ipy_escape_command.rs b/crates/ruff_python_formatter/src/statement/stmt_ipy_escape_command.rs index 539ab3a8d8a03..6af1da6ad2616 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_ipy_escape_command.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_ipy_escape_command.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use crate::prelude::*; use ruff_python_ast::{Ranged, StmtIpyEscapeCommand}; @@ -8,4 +9,12 @@ impl FormatNodeRule for FormatStmtIpyEscapeCommand { fn fmt_fields(&self, item: &StmtIpyEscapeCommand, f: &mut PyFormatter) -> FormatResult<()> { source_text_slice(item.range(), ContainsNewlines::No).fmt(f) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_match.rs b/crates/ruff_python_formatter/src/statement/stmt_match.rs index 9c58fcfe27bce..09347947d8e1c 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_match.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_match.rs @@ -1,4 +1,4 @@ -use ruff_formatter::{format_args, write, Buffer, FormatResult}; +use ruff_formatter::{format_args, write}; use ruff_python_ast::StmtMatch; use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment}; @@ -6,7 +6,7 @@ use crate::context::{NodeLevel, WithNodeLevel}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; -use crate::{FormatNodeRule, PyFormatter}; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatStmtMatch; diff --git a/crates/ruff_python_formatter/src/statement/stmt_nonlocal.rs b/crates/ruff_python_formatter/src/statement/stmt_nonlocal.rs index 777a7ac0d1499..c12f64af73d82 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_nonlocal.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_nonlocal.rs @@ -1,3 +1,4 @@ +use crate::comments::{SourceComment, SuppressionKind}; use ruff_formatter::{format_args, write}; use ruff_python_ast::node::AstNode; use ruff_python_ast::StmtNonlocal; @@ -50,4 +51,12 @@ impl FormatNodeRule for FormatStmtNonlocal { ) } } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_pass.rs b/crates/ruff_python_formatter/src/statement/stmt_pass.rs index 18afe6bbb6a65..b0cc9bd12345c 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_pass.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_pass.rs @@ -1,8 +1,9 @@ -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::text; -use ruff_formatter::{Format, FormatResult}; +use crate::comments::{SourceComment, SuppressionKind}; use ruff_python_ast::StmtPass; +use crate::prelude::*; +use crate::FormatNodeRule; + #[derive(Default)] pub struct FormatStmtPass; @@ -10,4 +11,12 @@ impl FormatNodeRule for FormatStmtPass { fn fmt_fields(&self, _item: &StmtPass, f: &mut PyFormatter) -> FormatResult<()> { text("pass").fmt(f) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_raise.rs b/crates/ruff_python_formatter/src/statement/stmt_raise.rs index 411b6e1b8f6d6..1f69f4084cab4 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_raise.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_raise.rs @@ -1,10 +1,11 @@ -use crate::expression::parentheses::Parenthesize; -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{space, text}; -use ruff_formatter::{write, Buffer, Format, FormatResult}; +use crate::comments::{SourceComment, SuppressionKind}; +use ruff_formatter::write; +use ruff_python_ast::StmtRaise; use crate::expression::maybe_parenthesize_expression; -use ruff_python_ast::StmtRaise; +use crate::expression::parentheses::Parenthesize; +use crate::prelude::*; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatStmtRaise; @@ -42,4 +43,12 @@ impl FormatNodeRule for FormatStmtRaise { } Ok(()) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_return.rs b/crates/ruff_python_formatter/src/statement/stmt_return.rs index 01b6e2bec9ff9..133f130e80e78 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_return.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_return.rs @@ -1,9 +1,11 @@ +use ruff_formatter::write; +use ruff_python_ast::StmtReturn; + +use crate::comments::{SourceComment, SuppressionKind}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{space, text}; -use ruff_formatter::{write, Buffer, Format, FormatResult}; -use ruff_python_ast::StmtReturn; +use crate::prelude::*; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatStmtReturn; @@ -24,4 +26,12 @@ impl FormatNodeRule for FormatStmtReturn { text("return").fmt(f) } } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_try.rs b/crates/ruff_python_formatter/src/statement/stmt_try.rs index be5e108088255..7213a3a027cac 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_try.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_try.rs @@ -1,9 +1,9 @@ -use ruff_formatter::FormatRuleWithOptions; -use ruff_formatter::{write, Buffer, FormatResult}; +use ruff_formatter::{write, FormatRuleWithOptions}; use ruff_python_ast::{ExceptHandler, Ranged, StmtTry, Suite}; use crate::comments; -use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment}; +use crate::comments::SourceComment; +use crate::comments::{leading_alternate_branch_comments, trailing_comments}; use crate::other::except_handler_except_handler::ExceptHandlerKind; use crate::prelude::*; use crate::statement::{FormatRefWithRule, Stmt}; diff --git a/crates/ruff_python_formatter/src/statement/stmt_try_star.rs b/crates/ruff_python_formatter/src/statement/stmt_try_star.rs deleted file mode 100644 index 8b137891791fe..0000000000000 --- a/crates/ruff_python_formatter/src/statement/stmt_try_star.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/ruff_python_formatter/src/statement/stmt_type_alias.rs b/crates/ruff_python_formatter/src/statement/stmt_type_alias.rs index 76e2528e54328..4c1be677189e5 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_type_alias.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_type_alias.rs @@ -1,10 +1,11 @@ +use crate::comments::{SourceComment, SuppressionKind}; +use ruff_formatter::write; +use ruff_python_ast::StmtTypeAlias; + use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; -use crate::AsFormat; -use crate::{FormatNodeRule, PyFormatter}; -use ruff_formatter::prelude::{space, text}; -use ruff_formatter::{write, Buffer, FormatResult}; -use ruff_python_ast::StmtTypeAlias; +use crate::prelude::*; +use crate::FormatNodeRule; #[derive(Default)] pub struct FormatStmtTypeAlias; @@ -34,4 +35,12 @@ impl FormatNodeRule for FormatStmtTypeAlias { ] ) } + + fn is_suppressed( + &self, + trailing_comments: &[SourceComment], + context: &PyFormatContext, + ) -> bool { + SuppressionKind::has_skip_comment(trailing_comments, context.source()) + } } diff --git a/crates/ruff_python_formatter/src/statement/stmt_while.rs b/crates/ruff_python_formatter/src/statement/stmt_while.rs index 504c6f8489296..0508278667ddc 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_while.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_while.rs @@ -1,11 +1,12 @@ +use ruff_formatter::write; +use ruff_python_ast::node::AstNode; +use ruff_python_ast::{Ranged, Stmt, StmtWhile}; + use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment}; use crate::expression::maybe_parenthesize_expression; use crate::expression::parentheses::Parenthesize; use crate::prelude::*; use crate::FormatNodeRule; -use ruff_formatter::write; -use ruff_python_ast::node::AstNode; -use ruff_python_ast::{Ranged, Stmt, StmtWhile}; #[derive(Default)] pub struct FormatStmtWhile; diff --git a/crates/ruff_python_formatter/src/statement/suite.rs b/crates/ruff_python_formatter/src/statement/suite.rs index 383589dde52fd..96e3b54d504e8 100644 --- a/crates/ruff_python_formatter/src/statement/suite.rs +++ b/crates/ruff_python_formatter/src/statement/suite.rs @@ -10,8 +10,9 @@ use crate::context::{NodeLevel, WithNodeLevel}; use crate::expression::expr_constant::ExprConstantLayout; use crate::expression::string::StringLayout; use crate::prelude::*; +use crate::statement::stmt_expr::FormatStmtExpr; use crate::verbatim::{ - write_suppressed_statements_starting_with_leading_comment, + suppressed_node, write_suppressed_statements_starting_with_leading_comment, write_suppressed_statements_starting_with_trailing_comment, }; @@ -399,27 +400,33 @@ impl<'a> DocstringStmt<'a> { impl Format> for DocstringStmt<'_> { fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { - // SAFETY: Safe because `DocStringStmt` guarantees that it only ever wraps a `ExprStmt` containing a `ConstantExpr`. - let constant = self - .0 - .as_expr_stmt() - .unwrap() - .value - .as_constant_expr() - .unwrap(); let comments = f.context().comments().clone(); - - // We format the expression, but the statement carries the comments - write!( - f, - [ - leading_comments(comments.leading_comments(self.0)), - constant - .format() - .with_options(ExprConstantLayout::String(StringLayout::DocString)), - trailing_comments(comments.trailing_comments(self.0)), - ] - ) + let node_comments = comments.leading_dangling_trailing_comments(self.0); + + if FormatStmtExpr.is_suppressed(node_comments.trailing, f.context()) { + suppressed_node(self.0).fmt(f) + } else { + // SAFETY: Safe because `DocStringStmt` guarantees that it only ever wraps a `ExprStmt` containing a `ConstantExpr`. + let constant = self + .0 + .as_expr_stmt() + .unwrap() + .value + .as_constant_expr() + .unwrap(); + + // We format the expression, but the statement carries the comments + write!( + f, + [ + leading_comments(node_comments.leading), + constant + .format() + .with_options(ExprConstantLayout::String(StringLayout::DocString)), + trailing_comments(node_comments.trailing), + ] + ) + } } } diff --git a/crates/ruff_python_formatter/src/verbatim.rs b/crates/ruff_python_formatter/src/verbatim.rs index fcb741c80bf75..d7bfa9beafc3f 100644 --- a/crates/ruff_python_formatter/src/verbatim.rs +++ b/crates/ruff_python_formatter/src/verbatim.rs @@ -882,3 +882,51 @@ impl Format> for VerbatimText { Ok(()) } } + +/// Disables formatting for `node` and instead uses the same formatting as the node has in source. +/// +/// The `node` gets indented as any formatted node to avoid syntax errors when the indentation string changes (e.g. from 2 spaces to 4). +/// The `node`s leading and trailing comments are formatted as usual, except if they fall into the suppressed node's range. +#[cold] +pub(crate) fn suppressed_node<'a, N>(node: N) -> FormatSuppressedNode<'a> +where + N: Into>, +{ + FormatSuppressedNode { node: node.into() } +} + +pub(crate) struct FormatSuppressedNode<'a> { + node: AnyNodeRef<'a>, +} + +impl Format> for FormatSuppressedNode<'_> { + fn fmt(&self, f: &mut Formatter>) -> FormatResult<()> { + let comments = f.context().comments().clone(); + let node_comments = comments.leading_dangling_trailing_comments(self.node); + + // Mark all comments as formatted that fall into the node range + for comment in node_comments.leading { + if comment.start() > self.node.start() { + comment.mark_formatted(); + } + } + + for comment in node_comments.trailing { + if comment.start() < self.node.end() { + comment.mark_formatted(); + } + } + + comments.mark_verbatim_node_comments_formatted(self.node); + + // Write the outer comments and format the node as verbatim + write!( + f, + [ + leading_comments(node_comments.leading), + verbatim_text(self.node, ContainsNewlines::Detect), + trailing_comments(node_comments.trailing) + ] + ) + } +} 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 614cee9816119..91f218dcb2a65 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 @@ -238,7 +238,7 @@ d={'a':1, # fmt: on -@@ -178,7 +180,7 @@ +@@ -178,14 +180,18 @@ $ """, # fmt: off @@ -247,6 +247,18 @@ d={'a':1, # fmt: on ) + + def single_literal_yapf_disable(): + """Black does not support this.""" +- BAZ = {(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)} # yapf: disable ++ BAZ = { ++ (1, 2, 3, 4), ++ (5, 6, 7, 8), ++ (9, 10, 11, 12) ++ } # yapf: disable + + + cfg.rule( ``` ## Ruff Output @@ -441,7 +453,11 @@ def long_lines(): def single_literal_yapf_disable(): """Black does not support this.""" - BAZ = {(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)} # yapf: disable + BAZ = { + (1, 2, 3, 4), + (5, 6, 7, 8), + (9, 10, 11, 12) + } # yapf: disable cfg.rule( diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip.py.snap deleted file mode 100644 index 3c9f58710b10f..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip.py.snap +++ /dev/null @@ -1,41 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py ---- -## Input - -```py -a, b = 1, 2 -c = 6 # fmt: skip -d = 5 -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,3 +1,3 @@ - a, b = 1, 2 --c = 6 # fmt: skip -+c = 6 # fmt: skip - d = 5 -``` - -## Ruff Output - -```py -a, b = 1, 2 -c = 6 # fmt: skip -d = 5 -``` - -## Black Output - -```py -a, b = 1, 2 -c = 6 # fmt: skip -d = 5 -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip2.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip2.py.snap deleted file mode 100644 index 6b686d5412daf..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip2.py.snap +++ /dev/null @@ -1,69 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip2.py ---- -## Input - -```py -l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"] -l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip -l3 = ["I have", "trailing comma", "so I should be braked",] -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -3,7 +3,11 @@ - "into multiple lines", - "because it is way too long", - ] --l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip -+l2 = [ -+ "But this list shouldn't", -+ "even though it also has", -+ "way too many characters in it", -+] # fmt: skip - l3 = [ - "I have", - "trailing comma", -``` - -## Ruff Output - -```py -l1 = [ - "This list should be broken up", - "into multiple lines", - "because it is way too long", -] -l2 = [ - "But this list shouldn't", - "even though it also has", - "way too many characters in it", -] # fmt: skip -l3 = [ - "I have", - "trailing comma", - "so I should be braked", -] -``` - -## Black Output - -```py -l1 = [ - "This list should be broken up", - "into multiple lines", - "because it is way too long", -] -l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip -l3 = [ - "I have", - "trailing comma", - "so I should be braked", -] -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip7.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip7.py.snap deleted file mode 100644 index cd13d5724870e..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip7.py.snap +++ /dev/null @@ -1,45 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip7.py ---- -## Input - -```py -a = "this is some code" -b = 5 #fmt:skip -c = 9 #fmt: skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,4 +1,4 @@ - a = "this is some code" --b = 5 # fmt:skip -+b = 5 # fmt:skip - c = 9 # fmt: skip - d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip -``` - -## Ruff Output - -```py -a = "this is some code" -b = 5 # fmt:skip -c = 9 # fmt: skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip -``` - -## Black Output - -```py -a = "this is some code" -b = 5 # fmt:skip -c = 9 # fmt: skip -d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_skip__decorators.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_skip__decorators.py.snap new file mode 100644 index 0000000000000..330e73752d3f2 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_skip__decorators.py.snap @@ -0,0 +1,49 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/decorators.py +--- +## Input +```py + +@FormattedDecorator(a =b) + # leading comment +@MyDecorator( # dangling comment + list = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12], x = some_other_function_call({ "test": "value", "more": "other"})) # fmt: skip + # leading class comment +class Test: + pass + + + +@FormattedDecorator(a =b) +# leading comment +@MyDecorator( # dangling comment + list = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12], x = some_other_function_call({ "test": "value", "more": "other"})) # fmt: skip +# leading class comment +def test(): + pass + +``` + +## Output +```py +@FormattedDecorator(a=b) +# leading comment +@MyDecorator( # dangling comment + list = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12], x = some_other_function_call({ "test": "value", "more": "other"})) # fmt: skip +# leading class comment +class Test: + pass + + +@FormattedDecorator(a=b) +# leading comment +@MyDecorator( # dangling comment + list = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12], x = some_other_function_call({ "test": "value", "more": "other"})) # fmt: skip +# leading class comment +def test(): + pass +``` + + + diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_skip__docstrings.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_skip__docstrings.py.snap new file mode 100644 index 0000000000000..ffb045fff81b5 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_skip__docstrings.py.snap @@ -0,0 +1,40 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/docstrings.py +--- +## Input +```py +def test(): + # leading comment + """ This docstring does not + get formatted + """ # fmt: skip + # trailing comment + +def test(): + # leading comment + """ This docstring gets formatted + """ # trailing comment + + and_this + gets + formatted + too +``` + +## Output +```py +def test(): + # leading comment + """ This docstring does not + get formatted + """ # fmt: skip + # trailing comment + + +def test(): + # leading comment + """This docstring gets formatted""" # trailing comment + + and_this + gets + formatted + too +``` + + +