From 4bc5eddf91ada7f0b3b80b52b0f5909183fade12 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 23 Aug 2023 00:40:18 -0400 Subject: [PATCH] Handle open-parenthesis comments on match case (#6798) ## Summary Ensures that we retain the open-parenthesis comment in cases like: ```python match pattern_comments: case ( # leading only_leading ): ... ``` Previously, this was treated as a leading comment on `only_leading`. ## Test Plan `cargo test` --- .../src/comments/placement.rs | 23 +++++++++++++++ .../src/other/match_case.rs | 28 ++++++++----------- ..._opening_parentheses_comment_value.py.snap | 3 +- .../snapshots/format@statement__match.py.snap | 3 +- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index ff686ba55a..f1c15cf405 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -210,6 +210,7 @@ fn handle_enclosed_comment<'a>( handle_leading_class_with_decorators_comment(comment, class_def) } AnyNodeRef::StmtImportFrom(import_from) => handle_import_from_comment(comment, import_from), + AnyNodeRef::MatchCase(match_case) => handle_match_case_comment(comment, match_case), AnyNodeRef::StmtWith(with_) => handle_with_comment(comment, with_), AnyNodeRef::ExprConstant(_) => { if let Some(AnyNodeRef::ExprFString(fstring)) = comment.enclosing_parent() { @@ -1303,6 +1304,28 @@ fn handle_import_from_comment<'a>( } } +/// Attach an enclosed end-of-line comment to a [`MatchCase`]. +/// +/// For example, given: +/// ```python +/// case ( # comment +/// pattern +/// ): +/// ... +/// ``` +/// +/// The comment will be attached to the [`MatchCase`] node as a dangling comment. +fn handle_match_case_comment<'a>( + comment: DecoratedComment<'a>, + match_case: &'a MatchCase, +) -> CommentPlacement<'a> { + if comment.line_position().is_end_of_line() && comment.start() < match_case.pattern.start() { + CommentPlacement::dangling(comment.enclosing_node(), comment) + } else { + CommentPlacement::Default(comment) + } +} + /// Attach an enclosed end-of-line comment to a [`ast::StmtWith`]. /// /// For example, given: diff --git a/crates/ruff_python_formatter/src/other/match_case.rs b/crates/ruff_python_formatter/src/other/match_case.rs index d638ac4a4f..c1992aa37b 100644 --- a/crates/ruff_python_formatter/src/other/match_case.rs +++ b/crates/ruff_python_formatter/src/other/match_case.rs @@ -1,9 +1,9 @@ -use ruff_formatter::{format_args, write, Buffer, FormatResult}; +use ruff_formatter::{write, Buffer, FormatResult}; use ruff_python_ast::{MatchCase, Pattern, Ranged}; use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer}; use ruff_text_size::TextRange; -use crate::comments::{leading_comments, SourceComment}; +use crate::comments::SourceComment; use crate::expression::parentheses::parenthesized; use crate::prelude::*; use crate::statement::clause::{clause_body, clause_header, ClauseHeader}; @@ -21,8 +21,13 @@ impl FormatNodeRule for FormatMatchCase { body, } = item; + // Distinguish dangling comments that appear on the open parenthesis from those that + // appear on the trailing colon. let comments = f.context().comments().clone(); let dangling_item_comments = comments.dangling(item); + let (open_parenthesis_comments, trailing_colon_comments) = dangling_item_comments.split_at( + dangling_item_comments.partition_point(|comment| comment.start() < pattern.start()), + ); write!( f, @@ -33,19 +38,10 @@ impl FormatNodeRule for FormatMatchCase { &format_with(|f| { write!(f, [text("case"), space()])?; - let leading_pattern_comments = comments.leading(pattern); - if !leading_pattern_comments.is_empty() { - parenthesized( - "(", - &format_args![ - leading_comments(leading_pattern_comments), - pattern.format() - ], - ")", - ) - .fmt(f)?; - } else if is_match_case_pattern_parenthesized(item, pattern, f.context())? { - parenthesized("(", &pattern.format(), ")").fmt(f)?; + if is_match_case_pattern_parenthesized(item, pattern, f.context())? { + parenthesized("(", &pattern.format(), ")") + .with_dangling_comments(open_parenthesis_comments) + .fmt(f)?; } else { pattern.format().fmt(f)?; } @@ -57,7 +53,7 @@ impl FormatNodeRule for FormatMatchCase { Ok(()) }), ), - clause_body(body, dangling_item_comments), + clause_body(body, trailing_colon_comments), ] ) } diff --git a/crates/ruff_python_formatter/tests/snapshots/format@parentheses__opening_parentheses_comment_value.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@parentheses__opening_parentheses_comment_value.py.snap index 706b92f950..0c287d1b0b 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@parentheses__opening_parentheses_comment_value.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@parentheses__opening_parentheses_comment_value.py.snap @@ -222,8 +222,7 @@ match ( # d 2 case d2: pass match d3: - case ( - # d 3 + case ( # d 3 x ): pass diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap index 8a643aca9b..e4857f40e4 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__match.py.snap @@ -354,8 +354,7 @@ match pattern_comments: match pattern_comments: - case ( - # leading + case ( # leading only_leading ): pass