Clean up comments.py preview mode

This commit is contained in:
Jelle Zijlstra 2022-12-10 08:36:35 -08:00
parent 96e62c57e3
commit f58f446640
3 changed files with 27 additions and 33 deletions

View File

@ -1088,7 +1088,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str:
future_imports = get_future_imports(src_node) future_imports = get_future_imports(src_node)
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, preview=mode.preview) normalize_fmt_off(src_node)
lines = LineGenerator(mode=mode) lines = LineGenerator(mode=mode)
elt = EmptyLineTracker(mode=mode) elt = EmptyLineTracker(mode=mode)
split_line_features = { split_line_features = {

View File

@ -29,7 +29,7 @@
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: " !:#'%"} COMMENT_EXCEPTIONS = " !:#'"
@dataclass @dataclass
@ -50,7 +50,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, *, preview: bool) -> Iterator[Leaf]: def generate_comments(leaf: LN) -> 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
@ -69,16 +69,12 @@ def generate_comments(leaf: LN, *, preview: bool) -> 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( for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER):
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( def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
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:
@ -104,7 +100,7 @@ def list_comments(
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, preview=preview) comment = make_comment(line)
result.append( result.append(
ProtoComment( ProtoComment(
type=comment_type, value=comment, newlines=nlines, consumed=consumed type=comment_type, value=comment, newlines=nlines, consumed=consumed
@ -114,7 +110,7 @@ def list_comments(
return result return result
def make_comment(content: str, *, preview: bool) -> str: def make_comment(content: str) -> 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
@ -135,26 +131,26 @@ def make_comment(content: str, *, preview: bool) -> 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 COMMENT_EXCEPTIONS[preview]: if content and content[0] not in COMMENT_EXCEPTIONS:
content = " " + content content = " " + content
return "#" + content return "#" + content
def normalize_fmt_off(node: Node, *, preview: bool) -> None: def normalize_fmt_off(node: Node) -> 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, preview=preview) try_again = convert_one_fmt_off_pair(node)
def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool: def convert_one_fmt_off_pair(node: Node) -> 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, preview=preview): for comment in list_comments(leaf.prefix, is_endmarker=False):
if comment.value not in FMT_PASS: if comment.value not in FMT_PASS:
previous_consumed = comment.consumed previous_consumed = comment.consumed
continue continue
@ -169,7 +165,7 @@ def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> 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, preview=preview)) ignored_nodes = list(generate_ignored_nodes(leaf, comment))
if not ignored_nodes: if not ignored_nodes:
continue continue
@ -214,26 +210,24 @@ def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool:
return False return False
def generate_ignored_nodes( def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
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.
Stops at the end of the block. Stops at the end of the block.
""" """
if comment.value in FMT_SKIP: if comment.value in FMT_SKIP:
yield from _generate_ignored_nodes_from_fmt_skip(leaf, comment, preview=preview) yield from _generate_ignored_nodes_from_fmt_skip(leaf, comment)
return return
container: Optional[LN] = container_of(leaf) container: Optional[LN] = container_of(leaf)
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, preview=preview): if is_fmt_on(container):
return return
# fix for fmt: on in children # fix for fmt: on in children
if children_contains_fmt_on(container, preview=preview): if children_contains_fmt_on(container):
for child in container.children: for child in container.children:
if isinstance(child, Leaf) and is_fmt_on(child, preview=preview): if isinstance(child, Leaf) and is_fmt_on(child):
if child.type in CLOSING_BRACKETS: if child.type in CLOSING_BRACKETS:
# This means `# fmt: on` is placed at a different bracket level # This means `# fmt: on` is placed at a different bracket level
# than `# fmt: off`. This is an invalid use, but as a courtesy, # than `# fmt: off`. This is an invalid use, but as a courtesy,
@ -241,7 +235,7 @@ def generate_ignored_nodes(
# The alternative is to fail the formatting. # The alternative is to fail the formatting.
yield child yield child
return return
if children_contains_fmt_on(child, preview=preview): if children_contains_fmt_on(child):
return return
yield child yield child
else: else:
@ -254,14 +248,14 @@ def generate_ignored_nodes(
def _generate_ignored_nodes_from_fmt_skip( def _generate_ignored_nodes_from_fmt_skip(
leaf: Leaf, comment: ProtoComment, *, preview: bool leaf: Leaf, comment: ProtoComment
) -> Iterator[LN]: ) -> Iterator[LN]:
"""Generate all leaves that should be ignored by the `# fmt: skip` from `leaf`.""" """Generate all leaves that should be ignored by the `# fmt: skip` from `leaf`."""
prev_sibling = leaf.prev_sibling prev_sibling = leaf.prev_sibling
parent = leaf.parent parent = leaf.parent
# Need to properly format the leaf prefix to compare it to comment.value, # Need to properly format the leaf prefix to compare it to comment.value,
# which is also formatted # which is also formatted
comments = list_comments(leaf.prefix, is_endmarker=False, preview=preview) comments = list_comments(leaf.prefix, is_endmarker=False)
if not comments or comment.value != comments[0].value: if not comments or comment.value != comments[0].value:
return return
if prev_sibling is not None: if prev_sibling is not None:
@ -295,12 +289,12 @@ def _generate_ignored_nodes_from_fmt_skip(
yield from iter(ignored_nodes) yield from iter(ignored_nodes)
def is_fmt_on(container: LN, preview: bool) -> bool: def is_fmt_on(container: LN) -> 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, preview=preview): for comment in list_comments(container.prefix, is_endmarker=False):
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:
@ -308,11 +302,11 @@ def is_fmt_on(container: LN, preview: bool) -> bool:
return fmt_on return fmt_on
def children_contains_fmt_on(container: LN, *, preview: bool) -> bool: def children_contains_fmt_on(container: LN) -> bool:
"""Determine if children have formatting switched on.""" """Determine if children have formatting switched on."""
for child in container.children: for child in container.children:
leaf = first_leaf_of(child) leaf = first_leaf_of(child)
if leaf is not None and is_fmt_on(leaf, preview=preview): if leaf is not None and is_fmt_on(leaf):
return True return True
return False return False

View File

@ -112,7 +112,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, preview=self.mode.preview): for comment in generate_comments(node):
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)
@ -971,7 +971,7 @@ def normalize_invisible_parens(
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, preview=preview): for pc in list_comments(node.prefix, is_endmarker=False):
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