From 1e6d1182bf4327265f2d0661eb3748e845277cec Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 23 Aug 2023 00:48:20 -0400 Subject: [PATCH] Improve comment handling around `PatternMatchAs` (#6797) ## Summary Follows up on https://github.com/astral-sh/ruff/pull/6652#discussion_r1300871033 with some modifications to the `PatternMatchAs` comment handling. Specifically, any comments between the `as` and the end are now formatted as dangling, and we now insert some newlines in the appropriate places. ## Test Plan `cargo test` --- .../test/fixtures/ruff/statement/match.py | 21 +++++++ .../src/comments/placement.rs | 42 ++++++++++++++ .../src/pattern/pattern_match_as.rs | 30 +++++++++- .../snapshots/format@statement__match.py.snap | 56 +++++++++++++++++-- 4 files changed, 142 insertions(+), 7 deletions(-) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py index 4f9d61e2b3..af41e9ec64 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/match.py @@ -168,6 +168,27 @@ match pattern_comments: pass +match pattern_comments: + case ( + pattern + # 1 + as + # 2 + name + # 3 + ): + pass + + +match subject: + case ( + pattern # 1 + as # 2 + name # 3 + ): + pass + + match x: case (a as b) as c: pass diff --git a/crates/ruff_python_formatter/src/comments/placement.rs b/crates/ruff_python_formatter/src/comments/placement.rs index f1c15cf405..49d4534131 100644 --- a/crates/ruff_python_formatter/src/comments/placement.rs +++ b/crates/ruff_python_formatter/src/comments/placement.rs @@ -205,6 +205,7 @@ fn handle_enclosed_comment<'a>( handle_module_level_own_line_comment_before_class_or_function_comment(comment, locator) } AnyNodeRef::WithItem(_) => handle_with_item_comment(comment, locator), + AnyNodeRef::PatternMatchAs(_) => handle_pattern_match_as_comment(comment, locator), AnyNodeRef::StmtFunctionDef(_) => handle_leading_function_with_decorators_comment(comment), AnyNodeRef::StmtClassDef(class_def) => { handle_leading_class_with_decorators_comment(comment, class_def) @@ -1145,6 +1146,47 @@ fn handle_with_item_comment<'a>( } } +/// Handles trailing comments after the `as` keyword of a pattern match item: +/// +/// ```python +/// case ( +/// pattern +/// as # dangling end of line comment +/// # dangling own line comment +/// name +/// ): ... +/// ``` +fn handle_pattern_match_as_comment<'a>( + comment: DecoratedComment<'a>, + locator: &Locator, +) -> CommentPlacement<'a> { + debug_assert!(comment.enclosing_node().is_pattern_match_as()); + + let Some(pattern) = comment.preceding_node() else { + return CommentPlacement::Default(comment); + }; + + let mut tokens = SimpleTokenizer::starts_at(pattern.end(), locator.contents()) + .skip_trivia() + .skip_while(|token| token.kind == SimpleTokenKind::RParen); + + let Some(as_token) = tokens + .next() + .filter(|token| token.kind == SimpleTokenKind::As) + else { + return CommentPlacement::Default(comment); + }; + + if comment.end() < as_token.start() { + // If before the `as` keyword, then it must be a trailing comment of the pattern. + CommentPlacement::trailing(pattern, comment) + } else { + // Otherwise, must be a dangling comment. (Any comments that follow the name will be + // trailing comments on the pattern match item, rather than enclosed by it.) + CommentPlacement::dangling(comment.enclosing_node(), comment) + } +} + /// Handles comments around the `:=` token in a named expression (walrus operator). /// /// For example, here, `# 1` and `# 2` will be marked as dangling comments on the named expression, diff --git a/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs b/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs index b6785bc279..fb969c6b4b 100644 --- a/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs +++ b/crates/ruff_python_formatter/src/pattern/pattern_match_as.rs @@ -1,6 +1,7 @@ use ruff_formatter::{write, Buffer, FormatResult}; use ruff_python_ast::{Pattern, PatternMatchAs}; +use crate::comments::{dangling_comments, SourceComment}; use crate::expression::parentheses::parenthesized; use crate::prelude::*; use crate::{FormatNodeRule, PyFormatter}; @@ -16,6 +17,8 @@ impl FormatNodeRule for FormatPatternMatchAs { name, } = item; + let comments = f.context().comments().clone(); + if let Some(name) = name { if let Some(pattern) = pattern { // Parenthesize nested `PatternMatchAs` like `(a as b) as c`. @@ -31,7 +34,24 @@ impl FormatNodeRule for FormatPatternMatchAs { pattern.format().fmt(f)?; } - write!(f, [space(), text("as"), space()])?; + if comments.has_trailing(pattern.as_ref()) { + write!(f, [hard_line_break()])?; + } else { + write!(f, [space()])?; + } + + write!(f, [text("as")])?; + + let trailing_as_comments = comments.dangling(item); + if trailing_as_comments.is_empty() { + write!(f, [space()])?; + } else if trailing_as_comments + .iter() + .all(|comment| comment.line_position().is_own_line()) + { + write!(f, [hard_line_break()])?; + } + write!(f, [dangling_comments(trailing_as_comments)])?; } name.format().fmt(f) } else { @@ -39,4 +59,12 @@ impl FormatNodeRule for FormatPatternMatchAs { text("_").fmt(f) } } + + fn fmt_dangling_comments( + &self, + _dangling_comments: &[SourceComment], + _f: &mut PyFormatter, + ) -> FormatResult<()> { + Ok(()) + } } 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 e4857f40e4..54d5e1be1c 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 @@ -174,6 +174,27 @@ match pattern_comments: pass +match pattern_comments: + case ( + pattern + # 1 + as + # 2 + name + # 3 + ): + pass + + +match subject: + case ( + pattern # 1 + as # 2 + name # 3 + ): + pass + + match x: case (a as b) as c: pass @@ -378,10 +399,11 @@ match pattern_comments: match pattern_comments: case ( # 1 - pattern as name # 2 + pattern # 2 # 3 - # 4 - # 5 # 6 + as # 4 + # 5 + name # 6 # 7 ): pass @@ -389,15 +411,37 @@ match pattern_comments: match pattern_comments: case ( - pattern as name + pattern # 1 - # 2 - # 3 # 4 + as # 2 + # 3 + name # 4 # 5 ): pass +match pattern_comments: + case ( + pattern + # 1 + as + # 2 + name + # 3 + ): + pass + + +match subject: + case ( + pattern # 1 + as # 2 + name # 3 + ): + pass + + match x: case (a as b) as c: pass