Support fmt: skip for simple-statements and decorators (#6561)

This commit is contained in:
Micha Reiser 2023-08-17 07:58:19 +02:00 committed by GitHub
parent e3ecbe660e
commit 4dc32a00d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 545 additions and 316 deletions

View file

@ -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 {

View file

@ -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);

View file

@ -94,7 +94,7 @@ impl FormatRule<Expr, PyFormatContext<'_>> 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 {

View file

@ -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())
]
)?;
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)?;
self.fmt_fields(node, f)?;
self.fmt_dangling_comments(node_comments.dangling, f)?;
write!(
f,
[
source_position(node.end()),
trailing_comments(node_comments.trailing)
]
)
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
let src = r#"
@MyDecorator(list = a) # fmt: skip
# trailing comment
class Test:
pass
a + b
# suppressed comments
a + b # formatted
"#;
// Tokenize once

View file

@ -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<Decorator> for FormatDecorator {
]
)
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<Stmt, PyFormatContext<'_>> 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),
}
}
}

View file

@ -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<StmtAnnAssign> for FormatStmtAnnAssign {
Ok(())
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<StmtAssert> for FormatStmtAssert {
Ok(())
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<StmtAssign> for FormatStmtAssign {
)]
)
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}
#[derive(Debug)]

View file

@ -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<StmtAugAssign> for FormatStmtAugAssign {
]
)
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<StmtBreak> 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())
}
}

View file

@ -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<StmtClassDef> 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<StmtClassDef> for FormatStmtClassDef {
Ok(())
}
}
pub(super) struct FormatDecorators<'a> {
pub(super) decorators: &'a [Decorator],
pub(super) leading_definition_comments: &'a [SourceComment],
}
impl Format<PyFormatContext<'_>> for FormatDecorators<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> 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(())
}
}

View file

@ -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<StmtContinue> 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())
}
}

View file

@ -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<StmtDelete> 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())
}
}

View file

@ -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<StmtExpr> 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 {

View file

@ -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);

View file

@ -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<StmtFunctionDef> 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<StmtFunctionDef> 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<StmtFunctionDef> 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<StmtFunctionDef> 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))
]
)
}

View file

@ -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<StmtGlobal> for FormatStmtGlobal {
)
}
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<StmtImport> 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())
}
}

View file

@ -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<StmtImportFrom> 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())
}
}

View file

@ -1,3 +1,4 @@
use crate::comments::{SourceComment, SuppressionKind};
use crate::prelude::*;
use ruff_python_ast::{Ranged, StmtIpyEscapeCommand};
@ -8,4 +9,12 @@ impl FormatNodeRule<StmtIpyEscapeCommand> 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())
}
}

View file

@ -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;

View file

@ -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<StmtNonlocal> for FormatStmtNonlocal {
)
}
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<StmtPass> 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())
}
}

View file

@ -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<StmtRaise> for FormatStmtRaise {
}
Ok(())
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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<StmtReturn> for FormatStmtReturn {
text("return").fmt(f)
}
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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};

View file

@ -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<StmtTypeAlias> for FormatStmtTypeAlias {
]
)
}
fn is_suppressed(
&self,
trailing_comments: &[SourceComment],
context: &PyFormatContext,
) -> bool {
SuppressionKind::has_skip_comment(trailing_comments, context.source())
}
}

View file

@ -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;

View file

@ -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<PyFormatContext<'_>> for DocstringStmt<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> 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();
let node_comments = comments.leading_dangling_trailing_comments(self.0);
// 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)),
]
)
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),
]
)
}
}
}

View file

@ -882,3 +882,51 @@ impl Format<PyFormatContext<'_>> 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<AnyNodeRef<'a>>,
{
FormatSuppressedNode { node: node.into() }
}
pub(crate) struct FormatSuppressedNode<'a> {
node: AnyNodeRef<'a>,
}
impl Format<PyFormatContext<'_>> for FormatSuppressedNode<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> 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)
]
)
}
}