Formatter: Add EmptyWithDanglingComments helper (#5951)
**Summary** Add a `EmptyWithDanglingComments` format helper that formats
comments inside empty parentheses, brackets or curly braces. Previously,
this was implemented separately, and partially incorrectly, for each use
case.
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.
**Test Plan** Added a regression test.
145 (from previously 149) instances of unstable formatting remaining.
```
$ cargo run --bin ruff_dev --release -- format-dev --stability-check --error-file formatter-ecosystem-errors.txt --multi-project target/checkouts > formatter-ecosystem-progress.txt
$ rg "Unstable formatting" target/formatter-ecosystem-errors.txt | wc -l
145
```
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use rustpython_parser::ast::{Expr, ExprCall, Ranged};
|
||||
|
||||
use crate::builders::empty_parenthesized_with_dangling_comments;
|
||||
use ruff_formatter::write;
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
|
||||
use crate::comments::dangling_comments;
|
||||
use crate::expression::expr_generator_exp::GeneratorExpParentheses;
|
||||
use crate::expression::parentheses::{
|
||||
parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
|
||||
@@ -34,14 +34,15 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
|
||||
// ```
|
||||
if args.is_empty() && keywords.is_empty() {
|
||||
let comments = f.context().comments().clone();
|
||||
let comments = comments.dangling_comments(item);
|
||||
return write!(
|
||||
f,
|
||||
[
|
||||
func.format(),
|
||||
text("("),
|
||||
dangling_comments(comments),
|
||||
text(")")
|
||||
empty_parenthesized_with_dangling_comments(
|
||||
text("("),
|
||||
comments.dangling_comments(item),
|
||||
text(")"),
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::comments::{dangling_node_comments, leading_comments};
|
||||
use crate::builders::empty_parenthesized_with_dangling_comments;
|
||||
use crate::comments::leading_comments;
|
||||
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
|
||||
use crate::prelude::*;
|
||||
use crate::FormatNodeRule;
|
||||
@@ -64,14 +65,13 @@ impl FormatNodeRule<ExprDict> for FormatExprDict {
|
||||
debug_assert_eq!(keys.len(), values.len());
|
||||
|
||||
if values.is_empty() {
|
||||
return write!(
|
||||
f,
|
||||
[
|
||||
&text("{"),
|
||||
block_indent(&dangling_node_comments(item)),
|
||||
&text("}"),
|
||||
]
|
||||
);
|
||||
let comments = f.context().comments().clone();
|
||||
return empty_parenthesized_with_dangling_comments(
|
||||
text("{"),
|
||||
comments.dangling_comments(item),
|
||||
text("}"),
|
||||
)
|
||||
.fmt(f);
|
||||
}
|
||||
|
||||
let format_pairs = format_with(|f| {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::comments::{dangling_comments, CommentLinePosition};
|
||||
use crate::builders::empty_parenthesized_with_dangling_comments;
|
||||
use crate::expression::parentheses::{parenthesized, NeedsParentheses, OptionalParentheses};
|
||||
use crate::prelude::*;
|
||||
use crate::FormatNodeRule;
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use rustpython_parser::ast::{ExprList, Ranged};
|
||||
|
||||
@@ -20,30 +19,9 @@ impl FormatNodeRule<ExprList> for FormatExprList {
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling_comments(item);
|
||||
|
||||
// The empty list is special because there can be dangling comments, and they can be in two
|
||||
// positions:
|
||||
// ```python
|
||||
// a3 = [ # end-of-line
|
||||
// # own line
|
||||
// ]
|
||||
// ```
|
||||
// In all other cases comments get assigned to a list element
|
||||
if elts.is_empty() {
|
||||
let end_of_line_split = dangling.partition_point(|comment| {
|
||||
comment.line_position() == CommentLinePosition::EndOfLine
|
||||
});
|
||||
debug_assert!(dangling[end_of_line_split..]
|
||||
.iter()
|
||||
.all(|comment| comment.line_position() == CommentLinePosition::OwnLine));
|
||||
return write!(
|
||||
f,
|
||||
[group(&format_args![
|
||||
text("["),
|
||||
dangling_comments(&dangling[..end_of_line_split]),
|
||||
soft_block_indent(&dangling_comments(&dangling[end_of_line_split..])),
|
||||
text("]")
|
||||
])]
|
||||
);
|
||||
return empty_parenthesized_with_dangling_comments(text("["), dangling, text("]"))
|
||||
.fmt(f);
|
||||
}
|
||||
|
||||
debug_assert!(
|
||||
|
||||
@@ -5,8 +5,7 @@ use rustpython_parser::ast::{Expr, Ranged};
|
||||
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
|
||||
use crate::builders::parenthesize_if_expands;
|
||||
use crate::comments::{dangling_comments, CommentLinePosition};
|
||||
use crate::builders::{empty_parenthesized_with_dangling_comments, parenthesize_if_expands};
|
||||
use crate::expression::parentheses::{
|
||||
parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
|
||||
};
|
||||
@@ -79,22 +78,12 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
||||
match elts.as_slice() {
|
||||
[] => {
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling_comments(item);
|
||||
let end_of_line_split = dangling.partition_point(|comment| {
|
||||
comment.line_position() == CommentLinePosition::EndOfLine
|
||||
});
|
||||
debug_assert!(dangling[end_of_line_split..]
|
||||
.iter()
|
||||
.all(|comment| comment.line_position() == CommentLinePosition::OwnLine));
|
||||
write!(
|
||||
f,
|
||||
[group(&format_args![
|
||||
text("("),
|
||||
dangling_comments(&dangling[..end_of_line_split]),
|
||||
soft_block_indent(&dangling_comments(&dangling[end_of_line_split..])),
|
||||
text(")")
|
||||
])]
|
||||
return empty_parenthesized_with_dangling_comments(
|
||||
text("("),
|
||||
comments.dangling_comments(item),
|
||||
text(")"),
|
||||
)
|
||||
.fmt(f);
|
||||
}
|
||||
[single] => match self.parentheses {
|
||||
TupleParentheses::Subscript
|
||||
|
||||
Reference in New Issue
Block a user