-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Adjust own-line comment placement between branches #21185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjust own-line comment placement between branches #21185
Conversation
|
c50d6c3 to
0e3bdc1
Compare
0e3bdc1 to
3aa47b8
Compare
else/finally clauseselse/finally clauses
3aa47b8 to
394793e
Compare
else/finally clauses
MichaReiser
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks much better to me and it's nice that this matches black in almost all cases.
However, there are a couple of tests that now fail because of instable formatting (the invariant format(source) == format(format(source)) doesn't hold anymore). I think it's because the fallback to preceding is too liberal.
| print("Do something") | ||
| while True: pass | ||
| # comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I often find it useful to number the comments. It makes it easier to match the input/output (and debug panics if we forgot to format a comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I didn't do that because there is only one comment in each example - do you want me to the number them across the whole fixture? Among these examples? Or just make them all #1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would give them unique numbers within the # Compare behavior with while/else comment placement section
| // else: | ||
| // pass | ||
| // ``` | ||
| let last_child = preceding.last_child_in_body().unwrap_or(preceding); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems risky. There isn't really anything enforcing that we're within a block statement. I think we, at least, want to assert that the enclosing node is a block statement.
|
Sorry about the failing tests! I made the beginner mistake of running tests, fixing snapshots, but then not re-running tests after that. Whoops! |
MichaReiser
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. This overall looks good. I just don't feel 100 confident about the new Non if matches case. Did you pick them individually or did you list all compound statements just to be sure? If it's the latter, than I'd prefer to only pick the once that are indeed necessary.
| None if matches!( | ||
| comment.enclosing_node(), | ||
| AnyNodeRef::StmtIf(_) | ||
| | AnyNodeRef::StmtWhile(_) | ||
| | AnyNodeRef::StmtFor(_) | ||
| | AnyNodeRef::StmtMatch(_) | ||
| | AnyNodeRef::ElifElseClause(_) | ||
| | AnyNodeRef::StmtTry(_) | ||
| | AnyNodeRef::MatchCase(_) | ||
| | AnyNodeRef::ExceptHandlerExceptHandler(_) | ||
| ) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use if following.is_first_statement_in_body() or even is_first_statement_in_alternate_body
I'm surprised that we need MatchCase and ExceptHandler here, as those nodes don't have alternate branches? How did you pick the nodes listed here? Did you add tests demonstrating that they're all necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sorry - I added all of those because that was my interpretation of
There isn't really anything enforcing that we're within a block statement. I think we, at least, want to assert that the enclosing node is a block statement.
But you're right that is_first_statement_in_alternate_body is more targeted, so I've changed it to that, thanks!
| print("Do something") | ||
| while True: pass | ||
| # comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would give them unique numbers within the # Compare behavior with while/else comment placement section
* origin/main: (26 commits) Mention `force-exclude` in "Configuration > Python file discovery" (#21500) Avoid syntax error when formatting attribute expressions with outer parentheses, parenthesized value, and trailing comment on value (#20418) [ty] suppress invalid suggestions in import statements (#21484) Limit `eglot-format` hook to eglot-managed Python buffers (#21459) Adjust own-line comment placement between branches (#21185) [ty] Subscript assignment diagnostics follow-up (#21452) [ty] Inlay hint call argument location (#20349) [ty] Use `CompactStr` for `StringLiteralType` (#21497) Update CodSpeedHQ/action action to v4.3.4 (#21488) Update salsa digest to a885bb4 (#21486) Update dependency ruff to v0.14.5 (#21489) Update astral-sh/setup-uv action to v7.1.3 (#21487) Update Rust crate get-size2 to v0.7.2 (#21490) Update Rust crate indicatif to v0.18.3 (#21491) Update Rust crate quick-junit to v0.5.2 (#21492) Update taiki-e/install-action action to v2.62.52 (#21493) Fix analyze graph tests on windows (#21481) `analyze`: Add option to skip over imports in `TYPE_CHECKING` blocks (#21472) [ty] Dataclasses: `__hash__` semantics and `unsafe_hash` (#21470) [ty] Dataclass transform: complete set of parameters (#21474) ...
* dcreager/coolable: (31 commits) mdformat don't panic Mention `force-exclude` in "Configuration > Python file discovery" (#21500) Avoid syntax error when formatting attribute expressions with outer parentheses, parenthesized value, and trailing comment on value (#20418) [ty] suppress invalid suggestions in import statements (#21484) known discrepancy TODO α-rename todo equiv too Limit `eglot-format` hook to eglot-managed Python buffers (#21459) Adjust own-line comment placement between branches (#21185) [ty] Subscript assignment diagnostics follow-up (#21452) [ty] Inlay hint call argument location (#20349) [ty] Use `CompactStr` for `StringLiteralType` (#21497) Update CodSpeedHQ/action action to v4.3.4 (#21488) Update salsa digest to a885bb4 (#21486) Update dependency ruff to v0.14.5 (#21489) Update astral-sh/setup-uv action to v7.1.3 (#21487) Update Rust crate get-size2 to v0.7.2 (#21490) Update Rust crate indicatif to v0.18.3 (#21491) Update Rust crate quick-junit to v0.5.2 (#21492) ...
## Summary This is another attempt at #21410 that fixes #19226. @MichaReiser helped me get something working in a very helpful pairing session. I pushed one additional commit moving the comments back from leading comments to trailing comments, which I think retains more of the input formatting. I was inspired by Dylan's PR (#21185) to make one of these tables: <table> <thead> <tr> <th scope="col">Input</th> <th scope="col">Main</th> <th scope="col">PR</th> </tr> </thead> <tbody> <tr> <td><pre lang="python"> if ( not # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( # comment not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( not # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> </tr> <tr> <td><pre lang="python"> if ( # unary comment not # operand comment ( # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ) ): pass </pre></td> <td><pre lang="python"> if ( # unary comment # operand comment not ( # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ) ): pass </pre></td> <td><pre lang="python"> if ( # unary comment not # operand comment ( # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ) ): pass </pre></td> </tr> <tr> <td><pre lang="python"> if ( not # comment aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( # comment not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> <td><pre lang="python"> if ( not aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # comment + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ): pass </pre></td> </tr> </tbody> </table> hopefully it helps even though the snippets are much wider here. The two main differences are (1) that we now retain own-line comments between the unary operator and its operand instead of moving these to leading comments on the operator itself, and (2) that we move end-of-line comments between the operator and operand to dangling end-of-line comments on the operand (the last example in the table). ## Test Plan Existing tests, plus new ones based on the issue. As I noted below, I also ran the output from main on the unary.py file back through this branch to check that we don't reformat code from main. This made me feel a bit better about not preview-gating the changes in this PR. ```shell > git show main:crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/unary.py | ruff format - | ./target/debug/ruff format --diff - > echo $? 0 ``` --------- Co-authored-by: Micha Reiser <micha@reiser.io> Co-authored-by: Takayuki Maeda <takoyaki0316@gmail.com>
This PR attempts to improve the placement of own-line comments between branches in the setting where the comment is more indented than the preceding node.
There are two main changes.
First change: Preceding node has leading content
If the preceding node has leading content, we now regard the comment as automatically less indented than the preceding node, and format accordingly.
For example,
This is more compatible with
black, although there is a (presumably very uncommon) edge case:I'm sort of okay with this - presumably if one wanted a comment for those semi-colon separated statements, one should have put it above them, and one wanted a comment only for
thatthen it ought to have been on the same line?Second change: searching for last child in body
While searching for the (recursively) last child in the body of the preceding branch, we implicitly assumed that the preceding node had to have a body to begin the recursion. But actually, in the base case, the preceding node is the last child in the body of the preceding branch. So, for example:
More examples
The table below is an attempt to summarize the changes in behavior. The rows alternate between an example snippet with
whileand the same example withif- in the former case we do not have anelsenode and in the latter we do.Notice that:
mainour handling ofifvs.whileis not consistent, whereas it is consistent in the present PRblackin all cases except that last example onmain, but agree in all cases for the present PR (though see above for a wonky edge case where we disagree).mainblack