This PR adjusts the logic for skipping formatting so that a `fmt: skip` can affect multiple statements if they lie on the same line. Specifically, a `fmt: skip` comment will now suppress all the statements in the suite in which it appears whose range intersects the line containing the skip directive. For example: ```python x=[ '1' ];x=2 # fmt: skip ``` remains unchanged after formatting. (Note that compound statements are somewhat special and were handled in a previous PR - see #20633). Closes #17331 and #11430. Simplest to review commit by commit - the key diffs of interest are the commit introducing the core logic, and the diff between the snapshots introduced in the last commit (compared to the second commit). # Implementation On `main` we format a suite of statements by iterating through them. If we meet a statement with a leading or trailing (own-line)`fmt: off` comment, then we suppress formatting until we meet a `fmt: on` comment. Otherwise we format the statement using its own formatting rule. How are `fmt: skip` comments handled then? They are handled internally to the formatting of each statement. Specifically, calling `.fmt` on a statement node will first check to see if there is a trailing, end-of-line `fmt: skip` (or `fmt: off`/`yapf: off`), and if so then write the node with suppressed formatting. In this PR we move the responsibility for handling `fmt: skip` into the formatting logic of the suite itself. This is done as follows: - Before beginning to format the suite, we do a pass through the statements and collect the data of ranges with skipped formatting. More specifically, we create a map with key given by the _first_ skipped statement in a block and value a pair consisting of the _last_ skipped statement and the _range_ to write verbatim. - We iterate as before, but if we meet a statement that is a key in the map constructed above, we pause to write the associated range verbatim. We then advance the iterator to the last statement in the block and proceed as before. ## Addendum on range formatting We also had to make some changes to range formatting in order to support this new behavior. For example, we want to make sure that ```python <RANGE_START>x=1<RANGE_END>;x=2 # fmt: skip ``` formats verbatim, rather than becoming ```python x = 1;x=2 # fmt: skip ``` Recall that range formatting proceeds in two steps: 1. Find the smallest enclosing node containing the range AND that has enough info to format the range (so it may be larger than you think, e.g. a docstring has enclosing node given by the suite, not the string itself.) 2. Carve out the formatted range from the result of formatting that enclosing node. We had to modify (1), since the suite knows how to format skipped nodes, but nodes may not "know" they are skipped. To do this we altered the `visit_body` bethod of the `FindEnclosingNode` visitor: now we iterate through the statements and check for skipped ranges intersecting the format range. If we find them, we return without descending. The result is to consider the statement containing the suite as the enclosing node in this case.
Ruff Formatter
The Ruff formatter is an extremely fast Python code formatter that ships as part of the ruff
CLI.
Goals
The formatter is designed to be a drop-in replacement for Black, but with an excessive focus on performance and direct integration with Ruff.
Specifically, the formatter is intended to emit near-identical output when run over Black-formatted code. When run over extensive Black-formatted projects like Django and Zulip, > 99.9% of lines are formatted identically. When migrating an existing project from Black to Ruff, you should expect to see a few differences on the margins, but the vast majority of your code should be unchanged.
If you identify deviations in your project, spot-check them against the intentional deviations enumerated below, as well as the unintentional deviations filed in the issue tracker. If you've identified a new deviation, please file an issue.
When run over non-Black-formatted code, the formatter makes some different decisions than Black, and so more deviations should be expected, especially around the treatment of end-of-line comments. For details, see Style Guide.
Getting started
Head to The Ruff Formatter for usage instructions and a comparison to Black.