Move empty_parenthesized into the parentheses.rs (#6403)

## Summary

This PR moves `empty_parenthesized` such that it's peer to
`parenthesized`, and changes the API to better match that of
`parenthesized` (takes `&str` rather than `StaticText`, has a
`with_dangling_comments` method, etc.).

It may be intentionally _not_ part of `parentheses.rs`, but to me
they're so similar that it makes more sense for them to be in the same
module, with the same API, etc.
This commit is contained in:
Charlie Marsh 2023-08-08 15:17:17 -04:00 committed by GitHub
parent fe9590f39f
commit c7703e205d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 96 deletions

View file

@ -4,9 +4,10 @@ use ruff_python_ast::Ranged;
use ruff_python_ast::{Expr, ExprDict};
use ruff_text_size::TextRange;
use crate::builders::empty_parenthesized_with_dangling_comments;
use crate::comments::leading_comments;
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
use crate::expression::parentheses::{
empty_parenthesized, parenthesized, NeedsParentheses, OptionalParentheses,
};
use crate::prelude::*;
use crate::FormatNodeRule;
@ -69,8 +70,7 @@ impl FormatNodeRule<ExprDict> for FormatExprDict {
let dangling = comments.dangling_comments(item);
if values.is_empty() {
return empty_parenthesized_with_dangling_comments(text("{"), dangling, text("}"))
.fmt(f);
return empty_parenthesized("{", dangling, "}").fmt(f);
}
let format_pairs = format_with(|f| {

View file

@ -1,9 +1,10 @@
use ruff_formatter::prelude::{format_with, text};
use ruff_formatter::prelude::format_with;
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::{ExprList, Ranged};
use crate::builders::empty_parenthesized_with_dangling_comments;
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
use crate::expression::parentheses::{
empty_parenthesized, parenthesized, NeedsParentheses, OptionalParentheses,
};
use crate::prelude::*;
use crate::FormatNodeRule;
@ -22,8 +23,7 @@ impl FormatNodeRule<ExprList> for FormatExprList {
let dangling = comments.dangling_comments(item);
if elts.is_empty() {
return empty_parenthesized_with_dangling_comments(text("["), dangling, text("]"))
.fmt(f);
return empty_parenthesized("[", dangling, "]").fmt(f);
}
let items = format_with(|f| {

View file

@ -4,8 +4,10 @@ use ruff_python_ast::ExprTuple;
use ruff_python_ast::{Expr, Ranged};
use ruff_text_size::TextRange;
use crate::builders::{empty_parenthesized_with_dangling_comments, parenthesize_if_expands};
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
use crate::builders::parenthesize_if_expands;
use crate::expression::parentheses::{
empty_parenthesized, parenthesized, NeedsParentheses, OptionalParentheses,
};
use crate::prelude::*;
#[derive(Eq, PartialEq, Debug, Default)]
@ -117,8 +119,7 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
// In all other cases comments get assigned to a list element
match elts.as_slice() {
[] => {
return empty_parenthesized_with_dangling_comments(text("("), dangling, text(")"))
.fmt(f);
return empty_parenthesized("(", dangling, ")").fmt(f);
}
[single] => match self.parentheses {
TupleParentheses::Preserve

View file

@ -4,7 +4,9 @@ use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::Ranged;
use ruff_python_trivia::{first_non_trivia_token, SimpleToken, SimpleTokenKind, SimpleTokenizer};
use crate::comments::{dangling_open_parenthesis_comments, SourceComment};
use crate::comments::{
dangling_comments, dangling_open_parenthesis_comments, trailing_comments, SourceComment,
};
use crate::context::{NodeLevel, WithNodeLevel};
use crate::prelude::*;
@ -307,6 +309,67 @@ impl<'ast> Format<PyFormatContext<'ast>> for FormatInParenthesesOnlyGroup<'_, 'a
}
}
/// Format comments inside empty parentheses, brackets or curly braces.
///
/// Empty `()`, `[]` and `{}` are special because there can be dangling comments, and they can be in
/// two positions:
/// ```python
/// x = [ # end-of-line
/// # own line
/// ]
/// ```
/// These comments are dangling because they can't be assigned to any element inside as they would
/// in all other cases.
pub(crate) fn empty_parenthesized<'content>(
left: &'static str,
comments: &'content [SourceComment],
right: &'static str,
) -> FormatEmptyParenthesized<'content> {
FormatEmptyParenthesized {
left,
comments,
right,
}
}
pub(crate) struct FormatEmptyParenthesized<'content> {
left: &'static str,
comments: &'content [SourceComment],
right: &'static str,
}
impl Format<PyFormatContext<'_>> for FormatEmptyParenthesized<'_> {
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
let end_of_line_split = self
.comments
.partition_point(|comment| comment.line_position().is_end_of_line());
debug_assert!(self.comments[end_of_line_split..]
.iter()
.all(|comment| comment.line_position().is_own_line()));
write!(
f,
[group(&format_args![
text(self.left),
// end-of-line comments
trailing_comments(&self.comments[..end_of_line_split]),
// Avoid unstable formatting with
// ```python
// x = () - (#
// )
// ```
// Without this the comment would go after the empty tuple first, but still expand
// the bin op. In the second formatting pass they are trailing bin op comments
// so the bin op collapse. Suboptimally we keep parentheses around the bin op in
// either case.
(!self.comments[..end_of_line_split].is_empty()).then_some(hard_line_break()),
// own line comments, which need to be indented
soft_block_indent(&dangling_comments(&self.comments[end_of_line_split..])),
text(self.right)
])]
)
}
}
#[cfg(test)]
mod tests {
use ruff_python_ast::node::AnyNodeRef;