[2213] Add support for single line format skip with other comments on the same line (#3959)
This commit is contained in:
parent
1d4c31aa58
commit
878937bcc3
@ -17,7 +17,7 @@
|
||||
|
||||
### Configuration
|
||||
|
||||
<!-- Changes to how Black can be configured -->
|
||||
- Add support for single line format skip with other comments on the same line (#3959)
|
||||
|
||||
### Packaging
|
||||
|
||||
|
@ -8,12 +8,14 @@ deliberately limited and rarely added. Previous formatting is taken into account
|
||||
little as possible, with rare exceptions like the magic trailing comma. The coding style
|
||||
used by _Black_ can be viewed as a strict subset of PEP 8.
|
||||
|
||||
_Black_ reformats entire files in place. It doesn't reformat lines that end with
|
||||
_Black_ reformats entire files in place. It doesn't reformat lines that contain
|
||||
`# fmt: skip` or blocks that start with `# fmt: off` and end with `# fmt: on`.
|
||||
`# fmt: on/off` must be on the same level of indentation and in the same block, meaning
|
||||
no unindents beyond the initial indentation level between them. It also recognizes
|
||||
[YAPF](https://github.com/google/yapf)'s block comments to the same effect, as a
|
||||
courtesy for straddling code.
|
||||
`# fmt: skip` can be mixed with other pragmas/comments either with multiple comments
|
||||
(e.g. `# fmt: skip # pylint # noqa`) or as a semicolon separeted list (e.g.
|
||||
`# fmt: skip; pylint; noqa`). `# fmt: on/off` must be on the same level of indentation
|
||||
and in the same block, meaning no unindents beyond the initial indentation level between
|
||||
them. It also recognizes [YAPF](https://github.com/google/yapf)'s block comments to the
|
||||
same effect, as a courtesy for straddling code.
|
||||
|
||||
The rest of this document describes the current formatting style. If you're interested
|
||||
in trying out where the style is heading, see [future style](./future_style.md) and try
|
||||
|
@ -1099,7 +1099,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str:
|
||||
for feature in {Feature.PARENTHESIZED_CONTEXT_MANAGERS}
|
||||
if supports_feature(versions, feature)
|
||||
}
|
||||
normalize_fmt_off(src_node)
|
||||
normalize_fmt_off(src_node, mode)
|
||||
lines = LineGenerator(mode=mode, features=context_manager_features)
|
||||
elt = EmptyLineTracker(mode=mode)
|
||||
split_line_features = {
|
||||
|
@ -3,6 +3,7 @@
|
||||
from functools import lru_cache
|
||||
from typing import Final, Iterator, List, Optional, Union
|
||||
|
||||
from black.mode import Mode, Preview
|
||||
from black.nodes import (
|
||||
CLOSING_BRACKETS,
|
||||
STANDALONE_COMMENT,
|
||||
@ -20,10 +21,11 @@
|
||||
|
||||
FMT_OFF: Final = {"# fmt: off", "# fmt:off", "# yapf: disable"}
|
||||
FMT_SKIP: Final = {"# fmt: skip", "# fmt:skip"}
|
||||
FMT_PASS: Final = {*FMT_OFF, *FMT_SKIP}
|
||||
FMT_ON: Final = {"# fmt: on", "# fmt:on", "# yapf: enable"}
|
||||
|
||||
COMMENT_EXCEPTIONS = " !:#'"
|
||||
_COMMENT_PREFIX = "# "
|
||||
_COMMENT_LIST_SEPARATOR = ";"
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -130,14 +132,14 @@ def make_comment(content: str) -> str:
|
||||
return "#" + content
|
||||
|
||||
|
||||
def normalize_fmt_off(node: Node) -> None:
|
||||
def normalize_fmt_off(node: Node, mode: Mode) -> None:
|
||||
"""Convert content between `# fmt: off`/`# fmt: on` into standalone comments."""
|
||||
try_again = True
|
||||
while try_again:
|
||||
try_again = convert_one_fmt_off_pair(node)
|
||||
try_again = convert_one_fmt_off_pair(node, mode)
|
||||
|
||||
|
||||
def convert_one_fmt_off_pair(node: Node) -> bool:
|
||||
def convert_one_fmt_off_pair(node: Node, mode: Mode) -> bool:
|
||||
"""Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment.
|
||||
|
||||
Returns True if a pair was converted.
|
||||
@ -145,21 +147,27 @@ 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 not in FMT_PASS:
|
||||
should_pass_fmt = comment.value in FMT_OFF or _contains_fmt_skip_comment(
|
||||
comment.value, mode
|
||||
)
|
||||
if not should_pass_fmt:
|
||||
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_PASS and comment.type != STANDALONE_COMMENT:
|
||||
if should_pass_fmt and comment.type != STANDALONE_COMMENT:
|
||||
prev = preceding_leaf(leaf)
|
||||
if prev:
|
||||
if comment.value in FMT_OFF and prev.type not in WHITESPACE:
|
||||
continue
|
||||
if comment.value in FMT_SKIP and prev.type in WHITESPACE:
|
||||
if (
|
||||
_contains_fmt_skip_comment(comment.value, mode)
|
||||
and prev.type in WHITESPACE
|
||||
):
|
||||
continue
|
||||
|
||||
ignored_nodes = list(generate_ignored_nodes(leaf, comment))
|
||||
ignored_nodes = list(generate_ignored_nodes(leaf, comment, mode))
|
||||
if not ignored_nodes:
|
||||
continue
|
||||
|
||||
@ -168,7 +176,7 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
|
||||
prefix = first.prefix
|
||||
if comment.value in FMT_OFF:
|
||||
first.prefix = prefix[comment.consumed :]
|
||||
if comment.value in FMT_SKIP:
|
||||
if _contains_fmt_skip_comment(comment.value, mode):
|
||||
first.prefix = ""
|
||||
standalone_comment_prefix = prefix
|
||||
else:
|
||||
@ -178,7 +186,7 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
|
||||
hidden_value = "".join(str(n) for n in ignored_nodes)
|
||||
if comment.value in FMT_OFF:
|
||||
hidden_value = comment.value + "\n" + hidden_value
|
||||
if comment.value in FMT_SKIP:
|
||||
if _contains_fmt_skip_comment(comment.value, mode):
|
||||
hidden_value += " " + comment.value
|
||||
if hidden_value.endswith("\n"):
|
||||
# That happens when one of the `ignored_nodes` ended with a NEWLINE
|
||||
@ -205,13 +213,15 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
|
||||
def generate_ignored_nodes(
|
||||
leaf: Leaf, comment: ProtoComment, mode: Mode
|
||||
) -> 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:
|
||||
if _contains_fmt_skip_comment(comment.value, mode):
|
||||
yield from _generate_ignored_nodes_from_fmt_skip(leaf, comment)
|
||||
return
|
||||
container: Optional[LN] = container_of(leaf)
|
||||
@ -327,3 +337,32 @@ def contains_pragma_comment(comment_list: List[Leaf]) -> bool:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _contains_fmt_skip_comment(comment_line: str, mode: Mode) -> bool:
|
||||
"""
|
||||
Checks if the given comment contains FMT_SKIP alone or paired with other comments.
|
||||
Matching styles:
|
||||
# fmt:skip <-- single comment
|
||||
# noqa:XXX # fmt:skip # a nice line <-- multiple comments (Preview)
|
||||
# pylint:XXX; fmt:skip <-- list of comments (; separated, Preview)
|
||||
"""
|
||||
semantic_comment_blocks = (
|
||||
[
|
||||
comment_line,
|
||||
*[
|
||||
_COMMENT_PREFIX + comment.strip()
|
||||
for comment in comment_line.split(_COMMENT_PREFIX)[1:]
|
||||
],
|
||||
*[
|
||||
_COMMENT_PREFIX + comment.strip()
|
||||
for comment in comment_line.strip(_COMMENT_PREFIX).split(
|
||||
_COMMENT_LIST_SEPARATOR
|
||||
)
|
||||
],
|
||||
]
|
||||
if Preview.single_line_format_skip_with_multiple_comments in mode
|
||||
else [comment_line]
|
||||
)
|
||||
|
||||
return any(comment in FMT_SKIP for comment in semantic_comment_blocks)
|
||||
|
@ -192,6 +192,7 @@ class Preview(Enum):
|
||||
fix_power_op_line_length = auto()
|
||||
hug_parens_with_braces_and_square_brackets = auto()
|
||||
allow_empty_first_line_before_new_block_or_comment = auto()
|
||||
single_line_format_skip_with_multiple_comments = auto()
|
||||
|
||||
|
||||
class Deprecated(UserWarning):
|
||||
|
@ -0,0 +1,20 @@
|
||||
# flags: --preview
|
||||
foo = 123 # fmt: skip # noqa: E501 # pylint
|
||||
bar = (
|
||||
123 ,
|
||||
( 1 + 5 ) # pylint # fmt:skip
|
||||
)
|
||||
baz = "a" + "b" # pylint; fmt: skip; noqa: E501
|
||||
skip_will_not_work = "a" + "b" # pylint fmt:skip
|
||||
skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it
|
||||
|
||||
# output
|
||||
|
||||
foo = 123 # fmt: skip # noqa: E501 # pylint
|
||||
bar = (
|
||||
123 ,
|
||||
( 1 + 5 ) # pylint # fmt:skip
|
||||
)
|
||||
baz = "a" + "b" # pylint; fmt: skip; noqa: E501
|
||||
skip_will_not_work = "a" + "b" # pylint fmt:skip
|
||||
skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it
|
Loading…
Reference in New Issue
Block a user