Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "# fmt: skip" directive to black #1800

Merged
merged 8 commits into from Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
86 changes: 46 additions & 40 deletions src/black/__init__.py
Expand Up @@ -2515,6 +2515,8 @@ def is_split_before_delimiter(leaf: Leaf, previous: Optional[Leaf] = None) -> Pr


FMT_OFF = {"# fmt: off", "# fmt:off", "# yapf: disable"}
FMT_SKIP = {"# fmt: skip", "# fmt:skip"}
FMT_PASS = {*FMT_OFF, *FMT_SKIP}
FMT_ON = {"# fmt: on", "# fmt:on", "# yapf: enable"}


Expand Down Expand Up @@ -5296,57 +5298,61 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
for leaf in node.leaves():
previous_consumed = 0
for comment in list_comments(leaf.prefix, is_endmarker=False):
if comment.value in FMT_OFF:
# We only want standalone comments. If there's no previous leaf or
# the previous leaf is indentation, it's a standalone comment in
# disguise.
if comment.type != STANDALONE_COMMENT:
prev = preceding_leaf(leaf)
if prev and prev.type not in WHITESPACE:
continue

ignored_nodes = list(generate_ignored_nodes(leaf))
if not ignored_nodes:
if comment.value not in FMT_PASS:
previous_consumed = comment.consumed
continue
# We only want standalone comments. If there's no previous leaf or
# the previous leaf is indentation, it's a standalone comment in
# disguise.
if comment.value in FMT_OFF and comment.type != STANDALONE_COMMENT:
prev = preceding_leaf(leaf)
if prev and prev.type not in WHITESPACE:
continue

first = ignored_nodes[0] # Can be a container node with the `leaf`.
parent = first.parent
prefix = first.prefix
first.prefix = prefix[comment.consumed :]
hidden_value = (
comment.value + "\n" + "".join(str(n) for n in ignored_nodes)
)
if hidden_value.endswith("\n"):
# That happens when one of the `ignored_nodes` ended with a NEWLINE
# leaf (possibly followed by a DEDENT).
hidden_value = hidden_value[:-1]
first_idx: Optional[int] = None
for ignored in ignored_nodes:
index = ignored.remove()
if first_idx is None:
first_idx = index
assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)"
assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)"
parent.insert_child(
first_idx,
Leaf(
STANDALONE_COMMENT,
hidden_value,
prefix=prefix[:previous_consumed] + "\n" * comment.newlines,
),
)
return True
ignored_nodes = list(generate_ignored_nodes(leaf, comment))
if not ignored_nodes:
continue

previous_consumed = comment.consumed
first = ignored_nodes[0] # Can be a container node with the `leaf`.
parent = first.parent
prefix = first.prefix
first.prefix = prefix[comment.consumed :]
hidden_value = "".join(str(n) for n in ignored_nodes)
if comment.value in FMT_OFF:
hidden_value = comment.value + "\n" + hidden_value
if hidden_value.endswith("\n"):
# That happens when one of the `ignored_nodes` ended with a NEWLINE
# leaf (possibly followed by a DEDENT).
hidden_value = hidden_value[:-1]
first_idx: Optional[int] = None
for ignored in ignored_nodes:
index = ignored.remove()
if first_idx is None:
first_idx = index
assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)"
assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)"
parent.insert_child(
first_idx,
Leaf(
STANDALONE_COMMENT,
hidden_value,
prefix=prefix[:previous_consumed] + "\n" * comment.newlines,
),
)
return True

return False


def generate_ignored_nodes(leaf: Leaf) -> Iterator[LN]:
def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
"""Starting from the container of `leaf`, generate all leaves until `# fmt: on`.

If comment is skip, returns leaf only.
Stops at the end of the block.
"""
if comment.value in FMT_SKIP and leaf.parent is not None:
yield leaf.parent
return
container: Optional[LN] = container_of(leaf)
while container is not None and container.type != token.ENDMARKER:
if is_fmt_on(container):
Expand Down
3 changes: 3 additions & 0 deletions tests/data/fmtskip.py
@@ -0,0 +1,3 @@
a, b = 1, 2
c = 6 # fmt: skip
d = 5
17 changes: 17 additions & 0 deletions tests/data/fmtskip2.py
@@ -0,0 +1,17 @@
l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"]
l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip
l3 = ["I have", "trailing comma", "so I should be braked",]

# output

l1 = [
"This list should be broken up",
"into multiple lines",
"because it is way too long",
]
l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip
l3 = [
"I have",
"trailing comma",
"so I should be braked",
]
20 changes: 20 additions & 0 deletions tests/data/fmtskip3.py
@@ -0,0 +1,20 @@
a = 3
# fmt: off
b, c = 1, 2
d = 6 # fmt: skip
e = 5
# fmt: on
f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."]

# output

a = 3
# fmt: off
b, c = 1, 2
d = 6 # fmt: skip
e = 5
# fmt: on
f = [
"This is a very long line that should be formatted into a clearer line ",
"by rearranging.",
]
3 changes: 3 additions & 0 deletions tests/test_format.py
Expand Up @@ -37,6 +37,9 @@
"fmtonoff2",
"fmtonoff3",
"fmtonoff4",
"fmtskip",
"fmtskip2",
"fmtskip3",
"fstring",
"function",
"function2",
Expand Down