parent
fa7f01592b
commit
f87df0e3c8
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
<!-- Changes that affect Black's preview style -->
|
<!-- Changes that affect Black's preview style -->
|
||||||
|
|
||||||
|
- Code cell separators `#%%` are now standardised to `# %%` (#2919)
|
||||||
|
|
||||||
### _Blackd_
|
### _Blackd_
|
||||||
|
|
||||||
<!-- Changes to blackd -->
|
<!-- Changes to blackd -->
|
||||||
|
@ -1166,7 +1166,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str:
|
|||||||
else:
|
else:
|
||||||
versions = detect_target_versions(src_node, future_imports=future_imports)
|
versions = detect_target_versions(src_node, future_imports=future_imports)
|
||||||
|
|
||||||
normalize_fmt_off(src_node)
|
normalize_fmt_off(src_node, preview=mode.preview)
|
||||||
lines = LineGenerator(mode=mode)
|
lines = LineGenerator(mode=mode)
|
||||||
elt = EmptyLineTracker(is_pyi=mode.is_pyi)
|
elt = EmptyLineTracker(is_pyi=mode.is_pyi)
|
||||||
empty_line = Line(mode=mode)
|
empty_line = Line(mode=mode)
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
FMT_PASS: Final = {*FMT_OFF, *FMT_SKIP}
|
FMT_PASS: Final = {*FMT_OFF, *FMT_SKIP}
|
||||||
FMT_ON: Final = {"# fmt: on", "# fmt:on", "# yapf: enable"}
|
FMT_ON: Final = {"# fmt: on", "# fmt:on", "# yapf: enable"}
|
||||||
|
|
||||||
|
COMMENT_EXCEPTIONS = {True: " !:#'", False: " !:#'%"}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtoComment:
|
class ProtoComment:
|
||||||
@ -42,7 +44,7 @@ class ProtoComment:
|
|||||||
consumed: int # how many characters of the original leaf's prefix did we consume
|
consumed: int # how many characters of the original leaf's prefix did we consume
|
||||||
|
|
||||||
|
|
||||||
def generate_comments(leaf: LN) -> Iterator[Leaf]:
|
def generate_comments(leaf: LN, *, preview: bool) -> Iterator[Leaf]:
|
||||||
"""Clean the prefix of the `leaf` and generate comments from it, if any.
|
"""Clean the prefix of the `leaf` and generate comments from it, if any.
|
||||||
|
|
||||||
Comments in lib2to3 are shoved into the whitespace prefix. This happens
|
Comments in lib2to3 are shoved into the whitespace prefix. This happens
|
||||||
@ -61,12 +63,16 @@ def generate_comments(leaf: LN) -> Iterator[Leaf]:
|
|||||||
Inline comments are emitted as regular token.COMMENT leaves. Standalone
|
Inline comments are emitted as regular token.COMMENT leaves. Standalone
|
||||||
are emitted with a fake STANDALONE_COMMENT token identifier.
|
are emitted with a fake STANDALONE_COMMENT token identifier.
|
||||||
"""
|
"""
|
||||||
for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER):
|
for pc in list_comments(
|
||||||
|
leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER, preview=preview
|
||||||
|
):
|
||||||
yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines)
|
yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines)
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=4096)
|
@lru_cache(maxsize=4096)
|
||||||
def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
|
def list_comments(
|
||||||
|
prefix: str, *, is_endmarker: bool, preview: bool
|
||||||
|
) -> List[ProtoComment]:
|
||||||
"""Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
|
"""Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
|
||||||
result: List[ProtoComment] = []
|
result: List[ProtoComment] = []
|
||||||
if not prefix or "#" not in prefix:
|
if not prefix or "#" not in prefix:
|
||||||
@ -92,7 +98,7 @@ def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
|
|||||||
comment_type = token.COMMENT # simple trailing comment
|
comment_type = token.COMMENT # simple trailing comment
|
||||||
else:
|
else:
|
||||||
comment_type = STANDALONE_COMMENT
|
comment_type = STANDALONE_COMMENT
|
||||||
comment = make_comment(line)
|
comment = make_comment(line, preview=preview)
|
||||||
result.append(
|
result.append(
|
||||||
ProtoComment(
|
ProtoComment(
|
||||||
type=comment_type, value=comment, newlines=nlines, consumed=consumed
|
type=comment_type, value=comment, newlines=nlines, consumed=consumed
|
||||||
@ -102,10 +108,10 @@ def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def make_comment(content: str) -> str:
|
def make_comment(content: str, *, preview: bool) -> str:
|
||||||
"""Return a consistently formatted comment from the given `content` string.
|
"""Return a consistently formatted comment from the given `content` string.
|
||||||
|
|
||||||
All comments (except for "##", "#!", "#:", '#'", "#%%") should have a single
|
All comments (except for "##", "#!", "#:", '#'") should have a single
|
||||||
space between the hash sign and the content.
|
space between the hash sign and the content.
|
||||||
|
|
||||||
If `content` didn't start with a hash sign, one is provided.
|
If `content` didn't start with a hash sign, one is provided.
|
||||||
@ -123,26 +129,26 @@ def make_comment(content: str) -> str:
|
|||||||
and not content.lstrip().startswith("type:")
|
and not content.lstrip().startswith("type:")
|
||||||
):
|
):
|
||||||
content = " " + content[1:] # Replace NBSP by a simple space
|
content = " " + content[1:] # Replace NBSP by a simple space
|
||||||
if content and content[0] not in " !:#'%":
|
if content and content[0] not in COMMENT_EXCEPTIONS[preview]:
|
||||||
content = " " + content
|
content = " " + content
|
||||||
return "#" + content
|
return "#" + content
|
||||||
|
|
||||||
|
|
||||||
def normalize_fmt_off(node: Node) -> None:
|
def normalize_fmt_off(node: Node, *, preview: bool) -> None:
|
||||||
"""Convert content between `# fmt: off`/`# fmt: on` into standalone comments."""
|
"""Convert content between `# fmt: off`/`# fmt: on` into standalone comments."""
|
||||||
try_again = True
|
try_again = True
|
||||||
while try_again:
|
while try_again:
|
||||||
try_again = convert_one_fmt_off_pair(node)
|
try_again = convert_one_fmt_off_pair(node, preview=preview)
|
||||||
|
|
||||||
|
|
||||||
def convert_one_fmt_off_pair(node: Node) -> bool:
|
def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool:
|
||||||
"""Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment.
|
"""Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment.
|
||||||
|
|
||||||
Returns True if a pair was converted.
|
Returns True if a pair was converted.
|
||||||
"""
|
"""
|
||||||
for leaf in node.leaves():
|
for leaf in node.leaves():
|
||||||
previous_consumed = 0
|
previous_consumed = 0
|
||||||
for comment in list_comments(leaf.prefix, is_endmarker=False):
|
for comment in list_comments(leaf.prefix, is_endmarker=False, preview=preview):
|
||||||
if comment.value not in FMT_PASS:
|
if comment.value not in FMT_PASS:
|
||||||
previous_consumed = comment.consumed
|
previous_consumed = comment.consumed
|
||||||
continue
|
continue
|
||||||
@ -157,7 +163,7 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
|
|||||||
if comment.value in FMT_SKIP and prev.type in WHITESPACE:
|
if comment.value in FMT_SKIP and prev.type in WHITESPACE:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ignored_nodes = list(generate_ignored_nodes(leaf, comment))
|
ignored_nodes = list(generate_ignored_nodes(leaf, comment, preview=preview))
|
||||||
if not ignored_nodes:
|
if not ignored_nodes:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -197,7 +203,9 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
|
def generate_ignored_nodes(
|
||||||
|
leaf: Leaf, comment: ProtoComment, *, preview: bool
|
||||||
|
) -> Iterator[LN]:
|
||||||
"""Starting from the container of `leaf`, generate all leaves until `# fmt: on`.
|
"""Starting from the container of `leaf`, generate all leaves until `# fmt: on`.
|
||||||
|
|
||||||
If comment is skip, returns leaf only.
|
If comment is skip, returns leaf only.
|
||||||
@ -221,13 +229,13 @@ def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
|
|||||||
yield leaf.parent
|
yield leaf.parent
|
||||||
return
|
return
|
||||||
while container is not None and container.type != token.ENDMARKER:
|
while container is not None and container.type != token.ENDMARKER:
|
||||||
if is_fmt_on(container):
|
if is_fmt_on(container, preview=preview):
|
||||||
return
|
return
|
||||||
|
|
||||||
# fix for fmt: on in children
|
# fix for fmt: on in children
|
||||||
if contains_fmt_on_at_column(container, leaf.column):
|
if contains_fmt_on_at_column(container, leaf.column, preview=preview):
|
||||||
for child in container.children:
|
for child in container.children:
|
||||||
if contains_fmt_on_at_column(child, leaf.column):
|
if contains_fmt_on_at_column(child, leaf.column, preview=preview):
|
||||||
return
|
return
|
||||||
yield child
|
yield child
|
||||||
else:
|
else:
|
||||||
@ -235,12 +243,12 @@ def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
|
|||||||
container = container.next_sibling
|
container = container.next_sibling
|
||||||
|
|
||||||
|
|
||||||
def is_fmt_on(container: LN) -> bool:
|
def is_fmt_on(container: LN, preview: bool) -> bool:
|
||||||
"""Determine whether formatting is switched on within a container.
|
"""Determine whether formatting is switched on within a container.
|
||||||
Determined by whether the last `# fmt:` comment is `on` or `off`.
|
Determined by whether the last `# fmt:` comment is `on` or `off`.
|
||||||
"""
|
"""
|
||||||
fmt_on = False
|
fmt_on = False
|
||||||
for comment in list_comments(container.prefix, is_endmarker=False):
|
for comment in list_comments(container.prefix, is_endmarker=False, preview=preview):
|
||||||
if comment.value in FMT_ON:
|
if comment.value in FMT_ON:
|
||||||
fmt_on = True
|
fmt_on = True
|
||||||
elif comment.value in FMT_OFF:
|
elif comment.value in FMT_OFF:
|
||||||
@ -248,7 +256,7 @@ def is_fmt_on(container: LN) -> bool:
|
|||||||
return fmt_on
|
return fmt_on
|
||||||
|
|
||||||
|
|
||||||
def contains_fmt_on_at_column(container: LN, column: int) -> bool:
|
def contains_fmt_on_at_column(container: LN, column: int, *, preview: bool) -> bool:
|
||||||
"""Determine if children at a given column have formatting switched on."""
|
"""Determine if children at a given column have formatting switched on."""
|
||||||
for child in container.children:
|
for child in container.children:
|
||||||
if (
|
if (
|
||||||
@ -257,7 +265,7 @@ def contains_fmt_on_at_column(container: LN, column: int) -> bool:
|
|||||||
or isinstance(child, Leaf)
|
or isinstance(child, Leaf)
|
||||||
and child.column == column
|
and child.column == column
|
||||||
):
|
):
|
||||||
if is_fmt_on(child):
|
if is_fmt_on(child, preview=preview):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -72,7 +72,7 @@ def visit_default(self, node: LN) -> Iterator[Line]:
|
|||||||
"""Default `visit_*()` implementation. Recurses to children of `node`."""
|
"""Default `visit_*()` implementation. Recurses to children of `node`."""
|
||||||
if isinstance(node, Leaf):
|
if isinstance(node, Leaf):
|
||||||
any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
|
any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
|
||||||
for comment in generate_comments(node):
|
for comment in generate_comments(node, preview=self.mode.preview):
|
||||||
if any_open_brackets:
|
if any_open_brackets:
|
||||||
# any comment within brackets is subject to splitting
|
# any comment within brackets is subject to splitting
|
||||||
self.current_line.append(comment)
|
self.current_line.append(comment)
|
||||||
@ -132,7 +132,7 @@ def visit_stmt(
|
|||||||
`parens` holds a set of string leaf values immediately after which
|
`parens` holds a set of string leaf values immediately after which
|
||||||
invisible parens should be put.
|
invisible parens should be put.
|
||||||
"""
|
"""
|
||||||
normalize_invisible_parens(node, parens_after=parens)
|
normalize_invisible_parens(node, parens_after=parens, preview=self.mode.preview)
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
if is_name_token(child) and child.value in keywords:
|
if is_name_token(child) and child.value in keywords:
|
||||||
yield from self.line()
|
yield from self.line()
|
||||||
@ -141,7 +141,7 @@ def visit_stmt(
|
|||||||
|
|
||||||
def visit_match_case(self, node: Node) -> Iterator[Line]:
|
def visit_match_case(self, node: Node) -> Iterator[Line]:
|
||||||
"""Visit either a match or case statement."""
|
"""Visit either a match or case statement."""
|
||||||
normalize_invisible_parens(node, parens_after=set())
|
normalize_invisible_parens(node, parens_after=set(), preview=self.mode.preview)
|
||||||
|
|
||||||
yield from self.line()
|
yield from self.line()
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
@ -802,7 +802,9 @@ def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
|
|||||||
leaf.prefix = ""
|
leaf.prefix = ""
|
||||||
|
|
||||||
|
|
||||||
def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
|
def normalize_invisible_parens(
|
||||||
|
node: Node, parens_after: Set[str], *, preview: bool
|
||||||
|
) -> None:
|
||||||
"""Make existing optional parentheses invisible or create new ones.
|
"""Make existing optional parentheses invisible or create new ones.
|
||||||
|
|
||||||
`parens_after` is a set of string leaf values immediately after which parens
|
`parens_after` is a set of string leaf values immediately after which parens
|
||||||
@ -811,7 +813,7 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
|
|||||||
Standardizes on visible parentheses for single-element tuples, and keeps
|
Standardizes on visible parentheses for single-element tuples, and keeps
|
||||||
existing visible parentheses for other tuples and generator expressions.
|
existing visible parentheses for other tuples and generator expressions.
|
||||||
"""
|
"""
|
||||||
for pc in list_comments(node.prefix, is_endmarker=False):
|
for pc in list_comments(node.prefix, is_endmarker=False, preview=preview):
|
||||||
if pc.value in FMT_OFF:
|
if pc.value in FMT_OFF:
|
||||||
# This `node` has a prefix with `# fmt: off`, don't mess with parens.
|
# This `node` has a prefix with `# fmt: off`, don't mess with parens.
|
||||||
return
|
return
|
||||||
@ -820,7 +822,9 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
|
|||||||
# Fixes a bug where invisible parens are not properly stripped from
|
# Fixes a bug where invisible parens are not properly stripped from
|
||||||
# assignment statements that contain type annotations.
|
# assignment statements that contain type annotations.
|
||||||
if isinstance(child, Node) and child.type == syms.annassign:
|
if isinstance(child, Node) and child.type == syms.annassign:
|
||||||
normalize_invisible_parens(child, parens_after=parens_after)
|
normalize_invisible_parens(
|
||||||
|
child, parens_after=parens_after, preview=preview
|
||||||
|
)
|
||||||
|
|
||||||
# Add parentheses around long tuple unpacking in assignments.
|
# Add parentheses around long tuple unpacking in assignments.
|
||||||
if (
|
if (
|
||||||
|
15
tests/data/comments8.py
Normal file
15
tests/data/comments8.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# The percent-percent comments are Spyder IDE cells.
|
||||||
|
# Both `#%%`` and `# %%` are accepted, so `black` standardises
|
||||||
|
# to the latter.
|
||||||
|
|
||||||
|
#%%
|
||||||
|
# %%
|
||||||
|
|
||||||
|
# output
|
||||||
|
|
||||||
|
# The percent-percent comments are Spyder IDE cells.
|
||||||
|
# Both `#%%`` and `# %%` are accepted, so `black` standardises
|
||||||
|
# to the latter.
|
||||||
|
|
||||||
|
# %%
|
||||||
|
# %%
|
@ -75,6 +75,7 @@
|
|||||||
# string processing
|
# string processing
|
||||||
"cantfit",
|
"cantfit",
|
||||||
"comments7",
|
"comments7",
|
||||||
|
"comments8",
|
||||||
"long_strings",
|
"long_strings",
|
||||||
"long_strings__edge_case",
|
"long_strings__edge_case",
|
||||||
"long_strings__regression",
|
"long_strings__regression",
|
||||||
|
Loading…
Reference in New Issue
Block a user