mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 20:42:10 +00:00
Format all attribute dot comments manually (#6825)
## Summary This PR modifies our formatting of comments around the `.` in an attribute. Specifically, the goal here is to avoid _reordering_ comments, and the net effect is that we generally leave comments where-they-are when dealing with comments between around the dot (which you can also think of as comments between attributes). All comments around the dot are now treated as dangling and formatted manually, with the exception of end-of-line or parenthesized comments on the value, like those marked as trailing here, which remain trailing: ```python ( ( a # trailing end-of-line # trailing own-line ) # dangling before dot end-of-line .b # trailing end-of-line ) ``` Closes https://github.com/astral-sh/ruff/issues/6823. ## Test Plan `cargo test` Before: | project | similarity index | |--------------|------------------| | cpython | 0.76050 | | django | 0.99820 | | transformers | 0.99800 | | twine | 0.99876 | | typeshed | 0.99953 | | warehouse | 0.99615 | | zulip | 0.99729 | After: | project | similarity index | |--------------|------------------| | cpython | 0.76050 | | django | 0.99820 | | transformers | 0.99800 | | twine | 0.99876 | | typeshed | 0.99953 | | warehouse | 0.99615 | | zulip | 0.99729 |
This commit is contained in:
parent
6f23469e00
commit
474e8fbcd4
5 changed files with 216 additions and 128 deletions
|
@ -1,8 +1,10 @@
|
|||
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::{Constant, Expr, ExprAttribute, ExprConstant};
|
||||
use ruff_python_ast::{Constant, Expr, ExprAttribute, ExprConstant, Ranged};
|
||||
use ruff_python_trivia::{find_only_token_in_range, SimpleTokenKind};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::comments::{leading_comments, trailing_comments, SourceComment};
|
||||
use crate::comments::{dangling_comments, trailing_comments, SourceComment};
|
||||
use crate::expression::parentheses::{
|
||||
is_expression_parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
|
||||
};
|
||||
|
@ -44,13 +46,6 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
|||
})
|
||||
);
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling_comments = comments.dangling(item);
|
||||
let leading_attribute_comments_start = dangling_comments
|
||||
.partition_point(|comment| comment.line_position().is_end_of_line());
|
||||
let (trailing_dot_comments, leading_attribute_comments) =
|
||||
dangling_comments.split_at(leading_attribute_comments_start);
|
||||
|
||||
if needs_parentheses {
|
||||
value.format().with_options(Parentheses::Always).fmt(f)?;
|
||||
} else if call_chain_layout == CallChainLayout::Fluent {
|
||||
|
@ -88,55 +83,54 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
|||
value.format().fmt(f)?;
|
||||
}
|
||||
|
||||
if comments.has_trailing_own_line(value.as_ref()) {
|
||||
hard_line_break().fmt(f)?;
|
||||
}
|
||||
|
||||
if call_chain_layout == CallChainLayout::Fluent {
|
||||
// Fluent style has line breaks before the dot
|
||||
// ```python
|
||||
// blogs3 = (
|
||||
// Blog.objects.filter(
|
||||
// entry__headline__contains="Lennon",
|
||||
// )
|
||||
// .filter(
|
||||
// entry__pub_date__year=2008,
|
||||
// )
|
||||
// .filter(
|
||||
// entry__pub_date__year=2008,
|
||||
// )
|
||||
// )
|
||||
// ```
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
(!leading_attribute_comments.is_empty()).then_some(hard_line_break()),
|
||||
leading_comments(leading_attribute_comments),
|
||||
text("."),
|
||||
trailing_comments(trailing_dot_comments),
|
||||
attr.format()
|
||||
]
|
||||
)
|
||||
// Identify dangling comments before and after the dot:
|
||||
// ```python
|
||||
// (
|
||||
// (
|
||||
// a
|
||||
// ) # `before_dot_end_of_line`
|
||||
// # `before_dot_own_line`
|
||||
// . # `after_dot_end_of_line`
|
||||
// # `after_dot_own_line`
|
||||
// b
|
||||
// )
|
||||
// ```
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling = comments.dangling(item);
|
||||
let (before_dot, after_dot) = if dangling.is_empty() {
|
||||
(dangling, dangling)
|
||||
} else {
|
||||
// Regular style
|
||||
// ```python
|
||||
// blogs2 = Blog.objects.filter(
|
||||
// entry__headline__contains="Lennon",
|
||||
// ).filter(
|
||||
// entry__pub_date__year=2008,
|
||||
// )
|
||||
// ```
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
text("."),
|
||||
trailing_comments(trailing_dot_comments),
|
||||
(!leading_attribute_comments.is_empty()).then_some(hard_line_break()),
|
||||
leading_comments(leading_attribute_comments),
|
||||
attr.format()
|
||||
]
|
||||
let dot_token = find_only_token_in_range(
|
||||
TextRange::new(item.value.end(), item.attr.start()),
|
||||
SimpleTokenKind::Dot,
|
||||
f.context().source(),
|
||||
);
|
||||
dangling.split_at(
|
||||
dangling.partition_point(|comment| comment.start() < dot_token.start()),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let (before_dot_end_of_line, before_dot_own_line) = before_dot.split_at(
|
||||
before_dot.partition_point(|comment| comment.line_position().is_end_of_line()),
|
||||
);
|
||||
|
||||
let (after_dot_end_of_line, after_dot_own_line) = after_dot.split_at(
|
||||
after_dot.partition_point(|comment| comment.line_position().is_end_of_line()),
|
||||
);
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
trailing_comments(before_dot_end_of_line),
|
||||
(!before_dot.is_empty()).then_some(hard_line_break()),
|
||||
dangling_comments(before_dot_own_line),
|
||||
text("."),
|
||||
trailing_comments(after_dot_end_of_line),
|
||||
(!after_dot.is_empty()).then_some(hard_line_break()),
|
||||
dangling_comments(after_dot_own_line),
|
||||
attr.format()
|
||||
]
|
||||
)
|
||||
});
|
||||
|
||||
let is_call_chain_root = self.call_chain_layout == CallChainLayout::Default
|
||||
|
@ -169,13 +163,7 @@ impl NeedsParentheses for ExprAttribute {
|
|||
== CallChainLayout::Fluent
|
||||
{
|
||||
OptionalParentheses::Multiline
|
||||
} else if context
|
||||
.comments()
|
||||
.dangling(self)
|
||||
.iter()
|
||||
.any(|comment| comment.line_position().is_own_line())
|
||||
|| context.comments().has_trailing_own_line(self)
|
||||
{
|
||||
} else if context.comments().has_dangling(self) {
|
||||
OptionalParentheses::Always
|
||||
} else {
|
||||
self.value.needs_parentheses(self.into(), context)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue