Skip to content

Preserve blank lines before a closing fmt: on comment - #5300

Open
alliasgher wants to merge 2 commits into
psf:mainfrom
alliasgher:fix-fmt-off-blank-lines
Open

Preserve blank lines before a closing fmt: on comment#5300
alliasgher wants to merge 2 commits into
psf:mainfrom
alliasgher:fix-fmt-off-blank-lines

Conversation

@alliasgher

Copy link
Copy Markdown

Description

Blank lines immediately before a closing # fmt: on are collapsed to the usual maximum, even though they are inside the opted-out region:

x = 1
# fmt: off
a = 1
                 # <- three blank lines
# fmt: on
y = 2

comes back with two. Reproduced on current main (74371e2).

convert_one_fmt_off_pair_handle_regular_fmt_block collapses the region into a single STANDALONE_COMMENT whose hidden_value is verbatim, which is why blanks between statements survive. But the blank run that terminates the region is not part of that value — it lives in the prefix of the following # fmt: on comment, which is owned by the normal blank-line machinery. So _maybe_empty_lines applies before = min(before, max_allowed) to it like any ordinary blank run.

I confirmed the mechanism by instrumenting EmptyLineTracker._maybe_empty_lines rather than reading it off: for the input above the # fmt: on line logs raw_nl=4 -> before=2.

The loss is one-shot — the output is stable on a second pass — which matches the report of a fresh diff every time the file is regenerated.

Scope

The issue bundles two defects and this takes only the first. The second symptom, blank lines lost at end of file, is a different mechanism (end-of-file newline normalisation, in generate_ignored_nodes) and is separately tracked as still-open #496. tests/util.read_data also strips the final newline, so it cannot be covered by a tests/data/cases fixture as-is. I did not want to bury an unrelated fix in this PR.

Fixes the mid-file half of #2877.

Checklist

  • Added a test case (tests/data/cases/fmtonoff9.py, no # output section, so it asserts the input is left untouched)
  • Added entry to CHANGES.md

Testing

I checked the fixture is not vacuous: with src/black/lines.py reverted, test_simple_format[fmtonoff9] fails; with it applied, it passes.

Covered in the fixture: three and four blank lines, nested inside a def, # yapf: disable/# yapf: enable, and the no-space # fmt:off/# fmt:on spelling. I also checked separately that a bare # fmt: on with no preceding # fmt: off is still capped normally — those blanks are not in an opted-out region, so they should be.

Full suite: 478 passed, 3 skipped. No existing fixture depended on the old behaviour (7 fmtonoff*, 3 line_ranges_fmt_off*, 20+ fmtskip* all unchanged). black --check on the two changed files is clean.

The blank run that terminates a fmt: off region lives in the prefix of the
following '# fmt: on' standalone comment, not in the verbatim block that
_handle_regular_fmt_block produces. _maybe_empty_lines therefore capped it
to max_allowed like any ordinary blank run, silently reformatting lines the
user had opted out of.

Skip the cap for a '# fmt: on' comment that closes a converted block.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant