Fix unstable formatting involving unwrapping multiple parentheses (#836) (#961)

This change also unwraps all unnecessary parentheses.
This commit is contained in:
Joe Antonakakis 2019-08-23 07:51:29 -07:00 committed by Zsolt Dollenstein
parent c9689b80fb
commit 3dc461a41a
2 changed files with 147 additions and 2 deletions

View File

@ -2882,7 +2882,15 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
if is_walrus_assignment(child): if is_walrus_assignment(child):
continue continue
if child.type == syms.atom: if child.type == syms.atom:
if maybe_make_parens_invisible_in_atom(child, parent=node): # Determines if the underlying atom should be surrounded with
# invisible params - also makes parens invisible recursively
# within the atom and removes repeated invisible parens within
# the atom
should_surround_with_parens = maybe_make_parens_invisible_in_atom(
child, parent=node
)
if should_surround_with_parens:
lpar = Leaf(token.LPAR, "") lpar = Leaf(token.LPAR, "")
rpar = Leaf(token.RPAR, "") rpar = Leaf(token.RPAR, "")
index = child.remove() or 0 index = child.remove() or 0
@ -2999,6 +3007,8 @@ def generate_ignored_nodes(leaf: Leaf) -> Iterator[LN]:
def maybe_make_parens_invisible_in_atom(node: LN, parent: LN) -> bool: def maybe_make_parens_invisible_in_atom(node: LN, parent: LN) -> bool:
"""If it's safe, make the parens in the atom `node` invisible, recursively. """If it's safe, make the parens in the atom `node` invisible, recursively.
Additionally, remove repeated, adjacent invisible parens from the atom `node`
as they are redundant.
Returns whether the node should itself be wrapped in invisible parentheses. Returns whether the node should itself be wrapped in invisible parentheses.
@ -3015,15 +3025,40 @@ def maybe_make_parens_invisible_in_atom(node: LN, parent: LN) -> bool:
first = node.children[0] first = node.children[0]
last = node.children[-1] last = node.children[-1]
if first.type == token.LPAR and last.type == token.RPAR: if first.type == token.LPAR and last.type == token.RPAR:
middle = node.children[1]
# make parentheses invisible # make parentheses invisible
first.value = "" # type: ignore first.value = "" # type: ignore
last.value = "" # type: ignore last.value = "" # type: ignore
maybe_make_parens_invisible_in_atom(node.children[1], parent=parent) maybe_make_parens_invisible_in_atom(middle, parent=parent)
if is_atom_with_invisible_parens(middle):
# Strip the invisible parens from `middle` by replacing
# it with the child in-between the invisible parens
middle.replace(middle.children[1])
return False return False
return True return True
def is_atom_with_invisible_parens(node: LN) -> bool:
"""Given a `LN`, determines whether it's an atom `node` with invisible
parens. Useful in dedupe-ing and normalizing parens.
"""
if isinstance(node, Leaf) or node.type != syms.atom:
return False
first, last = node.children[0], node.children[-1]
return (
isinstance(first, Leaf)
and first.type == token.LPAR
and first.value == ""
and isinstance(last, Leaf)
and last.type == token.RPAR
and last.value == ""
)
def is_empty_tuple(node: LN) -> bool: def is_empty_tuple(node: LN) -> bool:
"""Return True if `node` holds an empty tuple.""" """Return True if `node` holds an empty tuple."""
return ( return (

View File

@ -3,8 +3,118 @@
x = (1.2) x = (1.2)
(x) = (3) (x) = (3)
def example():
return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"))
def example1():
return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111))
def example1point5():
return ((((((1111111111111111111111111111111111111111111111111111111111111111111111111111111111111))))))
def example2():
return (("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"))
def example3():
return ((1111111111111111111111111111111111111111111111111111111111111111111111111111111))
def example4():
return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((True))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
def example5():
return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
def example6():
return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]})))))))))
def example7():
return ((((((((({a:a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20000000000000000000]})))))))))
def example8():
return (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((None)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
# output # output
print(1) print(1)
x = 1 x = 1
x = 1.2 x = 1.2
x = 3 x = 3
def example():
return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
def example1():
return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
def example1point5():
return 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
def example2():
return (
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
)
def example3():
return (
1111111111111111111111111111111111111111111111111111111111111111111111111111111
)
def example4():
return True
def example5():
return ()
def example6():
return {a: a for a in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]}
def example7():
return {
a: a
for a in [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20000000000000000000,
]
}
def example8():
return None