|
7 | 7 | from black.mode import Mode, Preview |
8 | 8 | from black.nodes import ( |
9 | 9 | CLOSING_BRACKETS, |
| 10 | + OPENING_BRACKETS, |
10 | 11 | STANDALONE_COMMENT, |
11 | 12 | STATEMENT, |
12 | 13 | WHITESPACE, |
@@ -645,7 +646,46 @@ def _generate_ignored_nodes_from_fmt_skip( |
645 | 646 | return |
646 | 647 |
|
647 | 648 | if Preview.fix_fmt_skip_in_one_liners in mode and not prev_sibling and parent: |
648 | | - prev_sibling = parent.prev_sibling |
| 649 | + # If the current leaf doesn't have a previous sibling, it might be deeply nested |
| 650 | + # (e.g. inside a list, function call, etc.). We need to climb up the tree |
| 651 | + # to find the previous sibling on the same line. |
| 652 | + |
| 653 | + # First, find the ancestor node that starts the current line |
| 654 | + # (has a newline in its prefix, or is at the root) |
| 655 | + line_start_node = parent |
| 656 | + while line_start_node.parent is not None: |
| 657 | + # The comment itself is in the leaf's prefix, so we need to check |
| 658 | + # if the current node's prefix (before the comment) has a newline |
| 659 | + node_prefix = ( |
| 660 | + line_start_node.prefix if hasattr(line_start_node, "prefix") else "" |
| 661 | + ) |
| 662 | + # Skip the comment part if this is the first node (parent) |
| 663 | + if line_start_node == parent: |
| 664 | + # The parent node has the comment in its prefix, so we check |
| 665 | + # what's before the comment |
| 666 | + comment_start = node_prefix.find(comment.value) |
| 667 | + if comment_start >= 0: |
| 668 | + prefix_before_comment = node_prefix[:comment_start] |
| 669 | + if "\n" in prefix_before_comment: |
| 670 | + # There's a newline before the comment, so this node |
| 671 | + # is at the start of the line |
| 672 | + break |
| 673 | + elif "\n" in node_prefix: |
| 674 | + break |
| 675 | + elif "\n" in node_prefix: |
| 676 | + # This node starts on a new line, so it's the line start |
| 677 | + break |
| 678 | + line_start_node = line_start_node.parent |
| 679 | + |
| 680 | + # Now find the prev_sibling by climbing from parent up to line_start_node |
| 681 | + curr = parent |
| 682 | + while curr is not None and curr != line_start_node.parent: |
| 683 | + if curr.prev_sibling is not None: |
| 684 | + prev_sibling = curr.prev_sibling |
| 685 | + break |
| 686 | + if curr.parent is None: |
| 687 | + break |
| 688 | + curr = curr.parent |
649 | 689 |
|
650 | 690 | if prev_sibling is not None: |
651 | 691 | leaf.prefix = leaf.prefix[comment.consumed :] |
@@ -746,6 +786,22 @@ def _generate_ignored_nodes_from_fmt_skip( |
746 | 786 | if header_nodes: |
747 | 787 | ignored_nodes = header_nodes + ignored_nodes |
748 | 788 |
|
| 789 | + # If the leaf's parent is an atom (parenthesized expression) and we've |
| 790 | + # captured the opening bracket in our ignored_nodes, we should include |
| 791 | + # the entire atom (including the closing bracket and the leaf itself) |
| 792 | + # to avoid partial formatting |
| 793 | + if ( |
| 794 | + parent is not None |
| 795 | + and parent.type == syms.atom |
| 796 | + and len(parent.children) >= 2 |
| 797 | + and parent.children[0].type in OPENING_BRACKETS |
| 798 | + and parent.children[0] in ignored_nodes |
| 799 | + ): |
| 800 | + # Replace the opening bracket and any other captured children of this atom |
| 801 | + # with the entire atom node |
| 802 | + ignored_nodes = [node for node in ignored_nodes if node.parent != parent] |
| 803 | + ignored_nodes.append(parent) |
| 804 | + |
749 | 805 | yield from ignored_nodes |
750 | 806 | elif ( |
751 | 807 | parent is not None and parent.type == syms.suite and leaf.type == token.NEWLINE |
|
0 commit comments