Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
line, where it no longer applies (#5618)
- Fix crash when a comment and a `# type: ignore` from the next line are joined onto one
line and ensure the `# type: ignore` comes first (#5625)
- Fix crash on consecutive statements that each have `# fmt: skip` on an opening bracket
(#5626)

### Preview style

Expand Down
18 changes: 18 additions & 0 deletions src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1989,9 +1989,27 @@ def _continues_fmt_skip_line(line: Line, leaf: Leaf) -> bool:
and _is_hidden_by_fmt_skip(leaf)
and not leaf.value.startswith("#")
and "\n" not in leaf.prefix
and not _starts_statement(leaf)
)


def _starts_statement(leaf: Leaf) -> bool:
"""Is `leaf` the first leaf of a statement?

The newline before a statement isn't in its prefix. It can even be hidden with
the statement above, e.g. `foo(a # fmt: skip` ... `)` followed by another one.
"""
node: LN = leaf
while node.parent is not None and node.parent.type not in {
syms.file_input,
syms.suite,
}:
if node.prev_sibling is not None:
return False
node = node.parent
return node.parent is not None


def _is_hidden_by_fmt_skip(leaf: Leaf) -> bool:
"""Is `leaf` a STANDALONE_COMMENT holding code hidden by `# fmt: skip` or
`# fmt: off`, rather than code left alone by `--line-ranges`?"""
Expand Down
97 changes: 97 additions & 0 deletions tests/data/cases/fmtskip_consecutive_statements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# A statement hidden by `# fmt: skip` takes its newline with it, so a hidden
# statement right after it must still start on a new line.

foo(a # fmt: skip
)
bar(b # fmt: skip
)

x = [1 # fmt: skip
]
y = [2 # fmt: skip
]

x = ( # fmt: skip
a
)
with ( # fmt: skip
b
):
x = 1

if foo(a # fmt: skip
):
pass
if ( # fmt: skip
a
):
pass

foo(a # fmt: skip
)
class A( # fmt: skip
b
): pass

foo(a # fmt: skip
)
@dec(b # fmt: skip
)
def f(): pass


def f():
foo(a # fmt: skip
)
return (b # fmt: skip
)

# output

# A statement hidden by `# fmt: skip` takes its newline with it, so a hidden
# statement right after it must still start on a new line.

foo(a # fmt: skip
)
bar(b # fmt: skip
)

x = [1 # fmt: skip
]
y = [2 # fmt: skip
]

x = ( # fmt: skip
a
)
with ( # fmt: skip
b
):
x = 1

if foo(a # fmt: skip
):
pass
if ( # fmt: skip
a
):
pass

foo(a # fmt: skip
)
class A( # fmt: skip
b
): pass

foo(a # fmt: skip
)
@dec(b # fmt: skip
)
def f(): pass


def f():
foo(a # fmt: skip
)
return (b # fmt: skip
)
Loading