def Visit_funcdef(self, node): # pylint: disable=invalid-name # funcdef ::= 'def' NAME parameters ['->' test] ':' suite # # Can't break before the function name and before the colon. The parameters # are handled by child iteration. colon_idx = 1 while pytree_utils.NodeName(node.children[colon_idx]) == 'simple_stmt': colon_idx += 1 _SetUnbreakable(node.children[colon_idx]) arrow_idx = -1 while colon_idx < len(node.children): if isinstance(node.children[colon_idx], pytree.Leaf): if node.children[colon_idx].value == ':': break if node.children[colon_idx].value == '->': arrow_idx = colon_idx colon_idx += 1 _SetUnbreakable(node.children[colon_idx]) self.DefaultNodeVisit(node) if arrow_idx > 0: _SetSplitPenalty( pytree_utils.LastLeafNode(node.children[arrow_idx - 1]), 0) _SetUnbreakable(node.children[arrow_idx]) _SetStronglyConnected(node.children[arrow_idx + 1])
def Visit_power(self, node): # pylint: disable=invalid-name,missing-docstring # power ::= atom trailer* ['**' factor] self.DefaultNodeVisit(node) # When atom is followed by a trailer, we can not break between them. # E.g. arr[idx] - no break allowed between 'arr' and '['. if (len(node.children) > 1 and pytree_utils.NodeName(node.children[1]) == 'trailer'): # children[1] itself is a whole trailer: we don't want to # mark all of it as unbreakable, only its first token: (, [ or . first = pytree_utils.FirstLeafNode(node.children[1]) if first.value != '.': _SetUnbreakable(node.children[1].children[0]) # A special case when there are more trailers in the sequence. Given: # atom tr1 tr2 # The last token of tr1 and the first token of tr2 comprise an unbreakable # region. For example: foo.bar.baz(1) # We can't put breaks between either of the '.', '(', or '[' and the names # *preceding* them. prev_trailer_idx = 1 while prev_trailer_idx < len(node.children) - 1: cur_trailer_idx = prev_trailer_idx + 1 cur_trailer = node.children[cur_trailer_idx] if pytree_utils.NodeName(cur_trailer) == 'trailer': # Now we know we have two trailers one after the other prev_trailer = node.children[prev_trailer_idx] if prev_trailer.children[-1].value != ')': # Set the previous node unbreakable if it's not a function call: # atom tr1() tr2 # It may be necessary (though undesirable) to split up a previous # function call's parentheses to the next line. _SetStronglyConnected(prev_trailer.children[-1]) _SetStronglyConnected(cur_trailer.children[0]) prev_trailer_idx = cur_trailer_idx else: break # We don't want to split before the last ')' of a function call. This also # takes care of the special case of: # atom tr1 tr2 ... trn # where the 'tr#' are trailers that may end in a ')'. for trailer in node.children[1:]: if pytree_utils.NodeName(trailer) != 'trailer': break if trailer.children[0].value in '([': if len(trailer.children) > 2: subtypes = pytree_utils.GetNodeAnnotation( trailer.children[0], pytree_utils.Annotation.SUBTYPE) if subtypes and format_token.Subtype.SUBSCRIPT_BRACKET in subtypes: _SetStronglyConnected( pytree_utils.FirstLeafNode(trailer.children[1])) last_child_node = pytree_utils.LastLeafNode(trailer) if last_child_node.value.strip().startswith('#'): last_child_node = last_child_node.prev_sibling if not (style.Get('INDENT_CLOSING_BRACKETS') or style.Get('DEDENT_CLOSING_BRACKETS')): last = pytree_utils.LastLeafNode(last_child_node.prev_sibling) if last.value != ',': if last_child_node.value == ']': _SetUnbreakable(last_child_node) else: _SetSplitPenalty(last_child_node, VERY_STRONGLY_CONNECTED) else: # If the trailer's children are '()', then make it a strongly # connected region. It's sometimes necessary, though undesirable, to # split the two. _SetStronglyConnected(trailer.children[-1])
def Visit_trailer(self, node): # pylint: disable=invalid-name # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME if node.children[0].value == '.': before = style.Get('SPLIT_BEFORE_DOT') _SetSplitPenalty(node.children[0], VERY_STRONGLY_CONNECTED if before else DOTTED_NAME) _SetSplitPenalty(node.children[1], DOTTED_NAME if before else VERY_STRONGLY_CONNECTED) elif len(node.children) == 2: # Don't split an empty argument list if at all possible. _SetSplitPenalty(node.children[1], VERY_STRONGLY_CONNECTED) elif len(node.children) == 3: name = pytree_utils.NodeName(node.children[1]) if name in {'argument', 'comparison'}: # Don't split an argument list with one element if at all possible. _SetStronglyConnected(node.children[1]) if (len(node.children[1].children) > 1 and pytree_utils.NodeName(node.children[1].children[1]) == 'comp_for'): # Don't penalize splitting before a comp_for expression. _SetSplitPenalty(pytree_utils.FirstLeafNode(node.children[1]), 0) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[1]), ONE_ELEMENT_ARGUMENT) elif (pytree_utils.NodeName(node.children[0]) == 'LSQB' and len(node.children[1].children) > 2 and (name.endswith('_test') or name.endswith('_expr'))): _SetStronglyConnected(node.children[1].children[0]) _SetStronglyConnected(node.children[1].children[2]) # Still allow splitting around the operator. split_before = ((name.endswith('_test') and style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR')) or (name.endswith('_expr') and style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'))) if split_before: _SetSplitPenalty( pytree_utils.LastLeafNode(node.children[1].children[1]), 0) else: _SetSplitPenalty( pytree_utils.FirstLeafNode(node.children[1].children[2]), 0) # Don't split the ending bracket of a subscript list. _SetVeryStronglyConnected(node.children[-1]) elif name not in { 'arglist', 'argument', 'term', 'or_test', 'and_test', 'comparison', 'atom', 'power' }: # Don't split an argument list with one element if at all possible. subtypes = pytree_utils.GetNodeAnnotation( pytree_utils.FirstLeafNode(node), pytree_utils.Annotation.SUBTYPE) if subtypes and format_token.Subtype.SUBSCRIPT_BRACKET in subtypes: _IncreasePenalty(node, SUBSCRIPT) # Bump up the split penalty for the first part of a subscript. We # would rather not split there. _IncreasePenalty(node.children[1], CONNECTED) else: _SetStronglyConnected(node.children[1], node.children[2]) if name == 'arglist': _SetStronglyConnected(node.children[-1]) self.DefaultNodeVisit(node)
def _InsertPseudoParentheses(node): """Insert pseudo parentheses so that dicts can be formatted correctly.""" comment_node = None if isinstance(node, pytree.Node): if node.children[-1].type == token.COMMENT: comment_node = node.children[-1].clone() node.children[-1].remove() first = pytree_utils.FirstLeafNode(node) last = pytree_utils.LastLeafNode(node) if first == last and first.type == token.COMMENT: # A comment was inserted before the value, which is a pytree.Leaf. # Encompass the dictionary's value into an ATOM node. last = first.next_sibling last_clone = last.clone() new_node = pytree.Node(syms.atom, [first.clone(), last_clone]) for orig_leaf, clone_leaf in zip(last.leaves(), last_clone.leaves()): pytree_utils.CopyYapfAnnotations(orig_leaf, clone_leaf) if hasattr(orig_leaf, 'is_pseudo'): clone_leaf.is_pseudo = orig_leaf.is_pseudo node.replace(new_node) node = new_node last.remove() first = pytree_utils.FirstLeafNode(node) last = pytree_utils.LastLeafNode(node) lparen = pytree.Leaf(token.LPAR, u'(', context=('', (first.get_lineno(), first.column - 1))) last_lineno = last.get_lineno() if last.type == token.STRING and '\n' in last.value: last_lineno += last.value.count('\n') if last.type == token.STRING and '\n' in last.value: last_column = len(last.value.split('\n')[-1]) + 1 else: last_column = last.column + len(last.value) + 1 rparen = pytree.Leaf(token.RPAR, u')', context=('', (last_lineno, last_column))) lparen.is_pseudo = True rparen.is_pseudo = True if isinstance(node, pytree.Node): node.insert_child(0, lparen) node.append_child(rparen) if comment_node: node.append_child(comment_node) _AppendFirstLeafTokenSubtype(node, format_token.Subtype.DICTIONARY_VALUE) else: clone = node.clone() for orig_leaf, clone_leaf in zip(node.leaves(), clone.leaves()): pytree_utils.CopyYapfAnnotations(orig_leaf, clone_leaf) new_node = pytree.Node(syms.atom, [lparen, clone, rparen]) node.replace(new_node) _AppendFirstLeafTokenSubtype(clone, format_token.Subtype.DICTIONARY_VALUE)