Skip to content

Commit

Permalink
Improve comment handling around PatternMatchAs (#6797)
Browse files Browse the repository at this point in the history
## Summary

Follows up on
#6652 (comment) 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`
  • Loading branch information
charliermarsh authored Aug 23, 2023
1 parent d08f697 commit 1e6d118
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,27 @@ def foo():
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
Expand Down
42 changes: 42 additions & 0 deletions crates/ruff_python_formatter/src/comments/placement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
30 changes: 29 additions & 1 deletion crates/ruff_python_formatter/src/pattern/pattern_match_as.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -16,6 +17,8 @@ impl FormatNodeRule<PatternMatchAs> 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`.
Expand All @@ -31,12 +34,37 @@ impl FormatNodeRule<PatternMatchAs> 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 {
debug_assert!(pattern.is_none());
text("_").fmt(f)
}
}

fn fmt_dangling_comments(
&self,
_dangling_comments: &[SourceComment],
_f: &mut PyFormatter,
) -> FormatResult<()> {
Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -378,26 +399,49 @@ 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
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
Expand Down

0 comments on commit 1e6d118

Please sign in to comment.