Exemple #1
0
 def Visit_atom(self, node):  # pylint: disable=invalid-name
     # atom ::= ('(' [yield_expr|testlist_gexp] ')'
     #           '[' [listmaker] ']' |
     #           '{' [dictsetmaker] '}')
     self.DefaultNodeVisit(node)
     if node.children[0].value == '(':
         if node.children[0].lineno == node.children[-1].lineno:
             self._SetExpressionPenalty(node, CONTIGUOUS_LIST)
         if node.children[-1].value == ')':
             if pytree_utils.NodeName(node.parent) == 'if_stmt':
                 pytree_utils.SetNodeAnnotation(
                     node.children[-1],
                     pytree_utils.Annotation.SPLIT_PENALTY, UNBREAKABLE)
             else:
                 pytree_utils.SetNodeAnnotation(
                     node.children[-1],
                     pytree_utils.Annotation.SPLIT_PENALTY,
                     STRONGLY_CONNECTED)
     elif node.children[0].value in '[{':
         # Keep empty containers together if we can.
         lbracket = node.children[0]
         rbracket = node.children[-1]
         if len(node.children) == 2:
             self._SetUnbreakable(node.children[-1])
         elif (rbracket.value in ']}'
               and lbracket.get_lineno() == rbracket.get_lineno()
               and rbracket.column - lbracket.column <
               style.Get('COLUMN_LIMIT')):
             self._SetExpressionPenalty(node, CONTIGUOUS_LIST)
def _AnnotateIndents(tree):
  """Annotate the tree with child_indent annotations.

  A child_indent annotation on a node specifies the indentation (as a string,
  like "  ") of its children. It is inferred from the INDENT child of a node.

  Arguments:
    tree: root of a pytree. The pytree is modified to add annotations to nodes.

  Raises:
    RuntimeError: if the tree is malformed.
  """
  # Annotate the root of the tree with zero indent.
  if tree.parent is None:
    pytree_utils.SetNodeAnnotation(tree, pytree_utils.Annotation.CHILD_INDENT,
                                   '')
  for child in tree.children:
    if child.type == token.INDENT:
      child_indent = pytree_utils.GetNodeAnnotation(
          tree, pytree_utils.Annotation.CHILD_INDENT)
      if child_indent is not None and child_indent != child.value:
        raise RuntimeError('inconsistent indentation for child', (tree, child))
      pytree_utils.SetNodeAnnotation(tree, pytree_utils.Annotation.CHILD_INDENT,
                                     child.value)
    _AnnotateIndents(child)
Exemple #3
0
 def Visit_comparison(self, node):  # pylint: disable=invalid-name
     # comparison ::= expr (comp_op expr)*
     self.DefaultNodeVisit(node)
     if len(node.children) == 3 and _StronglyConnectedCompOp(node):
         pytree_utils.SetNodeAnnotation(
             _FirstChildNode(node.children[1]),
             pytree_utils.Annotation.SPLIT_PENALTY, STRONGLY_CONNECTED)
         pytree_utils.SetNodeAnnotation(
             _FirstChildNode(node.children[2]),
             pytree_utils.Annotation.SPLIT_PENALTY, STRONGLY_CONNECTED)
     else:
         self._SetExpressionPenalty(node, COMPARISON_EXPRESSION)
Exemple #4
0
    def Visit_trailer(self, node):  # pylint: disable=invalid-name
        # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
        self.DefaultNodeVisit(node)
        if node.children[0].value == '.':
            self._SetUnbreakableOnChildren(node)
            pytree_utils.SetNodeAnnotation(
                node.children[1], pytree_utils.Annotation.SPLIT_PENALTY,
                DOTTED_NAME)
        elif len(node.children) == 2:
            # Don't split an empty argument list if at all possible.
            pytree_utils.SetNodeAnnotation(
                node.children[1], pytree_utils.Annotation.SPLIT_PENALTY,
                VERY_STRONGLY_CONNECTED)
        elif len(node.children) == 3:
            name = pytree_utils.NodeName(node.children[1])
            if name == 'power':
                if pytree_utils.NodeName(
                        node.children[1].children[0]) != 'atom':
                    # Don't split an argument list with one element if at all possible.
                    self._SetStronglyConnected(node.children[1],
                                               node.children[2])
                    pytree_utils.SetNodeAnnotation(
                        _FirstChildNode(node.children[1]),
                        pytree_utils.Annotation.SPLIT_PENALTY,
                        ONE_ELEMENT_ARGUMENT)
            elif (pytree_utils.NodeName(node.children[0]) == 'LSQB'
                  and (name.endswith('_test') or name.endswith('_expr'))):
                self._SetStronglyConnected(node.children[1].children[0])
                self._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:
                    pytree_utils.SetNodeAnnotation(
                        _LastChildNode(node.children[1].children[1]),
                        pytree_utils.Annotation.SPLIT_PENALTY, 0)
                else:
                    pytree_utils.SetNodeAnnotation(
                        _FirstChildNode(node.children[1].children[2]),
                        pytree_utils.Annotation.SPLIT_PENALTY, 0)

                # Don't split the ending bracket of a subscript list.
                self._SetVeryStronglyConnected(node.children[-1])
            elif name not in {
                    'arglist', 'argument', 'term', 'or_test', 'and_test',
                    'comparison', 'atom'
            }:
                # Don't split an argument list with one element if at all possible.
                self._SetStronglyConnected(node.children[1], node.children[2])
Exemple #5
0
 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
     self._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
     self._SetUnbreakable(node.children[colon_idx])
     self.DefaultNodeVisit(node)
     if arrow_idx > 0:
         pytree_utils.SetNodeAnnotation(
             _LastChildNode(node.children[arrow_idx - 1]),
             pytree_utils.Annotation.SPLIT_PENALTY, 0)
         self._SetUnbreakable(node.children[arrow_idx])
         self._SetStronglyConnected(node.children[arrow_idx + 1])
Exemple #6
0
    def Split(self):
        """Split the line at semicolons."""
        if not self.has_semicolon or self.disable:
            return [self]

        uwlines = []
        uwline = UnwrappedLine(self.depth)
        for tok in self._tokens:
            if tok.value == ';':
                uwlines.append(uwline)
                uwline = UnwrappedLine(self.depth)
            else:
                uwline.AppendToken(tok)

        if uwline.tokens:
            uwlines.append(uwline)

        for uwline in uwlines:
            pytree_utils.SetNodeAnnotation(uwline.first.node,
                                           pytree_utils.Annotation.MUST_SPLIT,
                                           True)
            uwline.first.previous_token = None
            uwline.last.next_token = None

        return uwlines
Exemple #7
0
 def _SetTokenSubtype(self, node, subtype, force=False):
     """Set the token's subtype only if it's not already set."""
     if force or not pytree_utils.GetNodeAnnotation(
             node, pytree_utils.Annotation.SUBTYPE):
         pytree_utils.SetNodeAnnotation(node,
                                        pytree_utils.Annotation.SUBTYPE,
                                        subtype)
Exemple #8
0
 def Visit_comp_if(self, node):  # pylint: disable=invalid-name
     # comp_if ::= 'if' old_test [comp_iter]
     pytree_utils.SetNodeAnnotation(node.children[0],
                                    pytree_utils.Annotation.SPLIT_PENALTY,
                                    0)
     self._SetStronglyConnected(*node.children[1:])
     self.DefaultNodeVisit(node)
Exemple #9
0
 def Visit_comp_for(self, node):  # pylint: disable=invalid-name
     # comp_for ::= 'for' exprlist 'in' testlist_safe [comp_iter]
     pytree_utils.SetNodeAnnotation(_FirstChildNode(node),
                                    pytree_utils.Annotation.SPLIT_PENALTY,
                                    0)
     self._SetStronglyConnected(*node.children[1:])
     self.DefaultNodeVisit(node)
def move_lines_to_index(uwline_index_to, lineno, uwlines, lines):
    """Method moves all lines in the list to the proper index of uwlines and
     update lineno on these lines. This is useful when you want to change the
     order of code lines. But note: it is not updating lineno on other lines

     @:returns positions (indexes) from original source where
      lines are taken from
    """
    # saving positions of imports here, that will be used for restoring 'lineno'
    lineno_where_line_was_taken_from = list()

    for line in lines:
        lineno_where_line_was_taken_from.append(line.lineno)
        for token in line.tokens:
            # here we will restore correct lineno for moved lines
            token.node.lineno = lineno
            # hack to remove newlines between imports that we moved to top
            pytree_utils.SetNodeAnnotation(token.node,
                                           pytree_utils.Annotation.NEWLINES, 0)
            lineno += get_lineno_delta(token)
        # need to update lineno on import lines to have consistency
        lineno += 1

    # filtering moved values and removing them from uwlines
    uwlines[:] = [line for line in uwlines if line not in lines]

    uwlines[uwline_index_to:uwline_index_to] = lines
    return lineno_where_line_was_taken_from
def _SetMustSplitOnFirstLeaf(node):
    """Set the "must split" annotation on the first leaf node."""
    def FindFirstLeaf(node):
        if isinstance(node, pytree.Leaf):
            return node
        return FindFirstLeaf(node.children[0])

    pytree_utils.SetNodeAnnotation(FindFirstLeaf(node),
                                   pytree_utils.Annotation.MUST_SPLIT, True)
Exemple #12
0
 def Visit_import_as_names(self, node):  # pyline: disable=invalid-name
     # import_as_names ::= import_as_name (',' import_as_name)* [',']
     self.DefaultNodeVisit(node)
     prev_child = None
     for child in node.children:
         if (prev_child and isinstance(prev_child, pytree.Leaf)
                 and prev_child.value == ','):
             pytree_utils.SetNodeAnnotation(
                 child, pytree_utils.Annotation.SPLIT_PENALTY,
                 style.Get('SPLIT_PENALTY_IMPORT_NAMES'))
         prev_child = child
Exemple #13
0
 def Visit_and_test(self, node):  # pylint: disable=invalid-name
     # and_test ::= not_test ('and' not_test)*
     self.DefaultNodeVisit(node)
     _SetExpressionPenalty(node, AND_TEST)
     index = 1
     while index + 1 < len(node.children):
         if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
             pytree_utils.SetNodeAnnotation(
                 _FirstChildNode(node.children[index]),
                 pytree_utils.Annotation.SPLIT_PENALTY, 0)
             pytree_utils.SetNodeAnnotation(
                 _FirstChildNode(node.children[index + 1]),
                 pytree_utils.Annotation.SPLIT_PENALTY, STRONGLY_CONNECTED)
         else:
             pytree_utils.SetNodeAnnotation(
                 _FirstChildNode(node.children[index]),
                 pytree_utils.Annotation.SPLIT_PENALTY, STRONGLY_CONNECTED)
             pytree_utils.SetNodeAnnotation(
                 _FirstChildNode(node.children[index + 1]),
                 pytree_utils.Annotation.SPLIT_PENALTY, 0)
         index += 2
Exemple #14
0
 def Visit_atom(self, node):  # pylint: disable=invalid-name
     # atom ::= ('(' [yield_expr|testlist_gexp] ')'
     #           '[' [listmaker] ']' |
     #           '{' [dictsetmaker] '}')
     self.DefaultNodeVisit(node)
     if node.children[0].value == '(':
         if node.children[-1].value == ')':
             if pytree_utils.NodeName(node.parent) == 'if_stmt':
                 pytree_utils.SetNodeAnnotation(
                     node.children[-1],
                     pytree_utils.Annotation.SPLIT_PENALTY, UNBREAKABLE)
             else:
                 pytree_utils.SetNodeAnnotation(
                     node.children[-1],
                     pytree_utils.Annotation.SPLIT_PENALTY,
                     COMPARISON_EXPRESSION)
     elif node.children[0].value in '[{':
         # Keep empty containers together if we can.
         lbracket = node.children[0]
         rbracket = node.children[-1]
         if len(node.children) == 2:
             _SetUnbreakable(node.children[-1])
Exemple #15
0
        def RecArithmeticExpression(node, first_child_leaf):
            if node is first_child_leaf:
                return

            if isinstance(node, pytree.Leaf):
                if node.value in {'(', 'for', 'if'}:
                    return
                penalty_annotation = pytree_utils.GetNodeAnnotation(
                    node, pytree_utils.Annotation.SPLIT_PENALTY, default=0)
                if penalty_annotation < penalty:
                    pytree_utils.SetNodeAnnotation(
                        node, pytree_utils.Annotation.SPLIT_PENALTY, penalty)
            else:
                for child in node.children:
                    RecArithmeticExpression(child, first_child_leaf)
Exemple #16
0
 def Visit_atom(self, node):  # pylint: disable=invalid-name
     # atom ::= ('(' [yield_expr|testlist_gexp] ')'
     #           '[' [listmaker] ']' |
     #           '{' [dictsetmaker] '}')
     self.DefaultNodeVisit(node)
     if node.children[0].value == '(':
         if node.children[0].lineno == node.children[-1].lineno:
             self._SetExpressionPenalty(node, CONTIGUOUS_LIST)
         if (pytree_utils.NodeName(node.parent) == 'if_stmt'
                 and node.children[-1].value == ')'):
             pytree_utils.SetNodeAnnotation(
                 node.children[-1], pytree_utils.Annotation.SPLIT_PENALTY,
                 UNBREAKABLE)
     elif node.children[0].value in '[{':
         # Keep empty containers together if we can.
         if len(node.children) == 2:
             self._SetStronglyConnected(node.children[-1])
Exemple #17
0
def _RecAnnotate(tree, annotate_name, annotate_value):
  """Recursively set the given annotation on all leafs of the subtree.

  Takes care to only increase the penalty. If the node already has a higher
  or equal penalty associated with it, this is a no-op.

  Args:
    tree: subtree to annotate
    annotate_name: name of the annotation to set
    annotate_value: value of the annotation to set
  """
  for child in tree.children:
    _RecAnnotate(child, annotate_name, annotate_value)
  if isinstance(tree, pytree.Leaf):
    cur_annotate = pytree_utils.GetNodeAnnotation(
        tree, annotate_name, default=0)
    if cur_annotate < annotate_value:
      pytree_utils.SetNodeAnnotation(tree, annotate_name, annotate_value)
Exemple #18
0
def _AllowBuilderStyleCalls(node):
    """Allow splitting before '.' if it's a builder style function call."""
    def RecGetLeaves(node):
        if isinstance(node, pytree.Leaf):
            return [node]
        children = []
        for child in node.children:
            children += RecGetLeaves(child)
        return children

    list_of_children = RecGetLeaves(node)
    prev_child = None
    for child in list_of_children:
        if child.value == '.':
            if prev_child.lineno != child.lineno:
                pytree_utils.SetNodeAnnotation(
                    child, pytree_utils.Annotation.SPLIT_PENALTY, 0)
        prev_child = child
Exemple #19
0
    def testMultiple(self):
        pytree_utils.SetNodeAnnotation(self._leaf, _FOO, 20)
        pytree_utils.SetNodeAnnotation(self._leaf, _FOO1, 1)
        pytree_utils.SetNodeAnnotation(self._leaf, _FOO2, 2)
        pytree_utils.SetNodeAnnotation(self._leaf, _FOO3, 3)
        pytree_utils.SetNodeAnnotation(self._leaf, _FOO4, 4)
        pytree_utils.SetNodeAnnotation(self._leaf, _FOO5, 5)

        self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO1), 1)
        self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO2), 2)
        self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO3), 3)
        self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO4), 4)
        self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO5), 5)
Exemple #20
0
 def Visit_trailer(self, node):  # pylint: disable=invalid-name
     # trailer ::= '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
     self.DefaultNodeVisit(node)
     if node.children[0].value == '.':
         self._SetUnbreakableOnChildren(node)
         pytree_utils.SetNodeAnnotation(
             node.children[1], pytree_utils.Annotation.SPLIT_PENALTY,
             DOTTED_NAME)
     elif len(node.children) == 2:
         # Don't split an empty argument list if at all possible.
         self._SetStronglyConnected(node.children[1])
     elif len(node.children) == 3:
         if (pytree_utils.NodeName(node.children[1])
                 not in {'arglist', 'argument', 'term'}):
             # Don't split an argument list with one element if at all possible.
             self._SetStronglyConnected(node.children[1], node.children[2])
         if pytree_utils.NodeName(node.children[-1]) == 'RSQB':
             # Don't split the ending bracket of a subscript list.
             self._SetStronglyConnected(node.children[-1])
Exemple #21
0
def _AdjustSplitPenalty(uwline):
  """Visit the node and adjust the split penalties if needed.

  A token shouldn't be split if it's not within a bracket pair. Mark any token
  that's not within a bracket pair as "unbreakable".

  Arguments:
    uwline: (UnwrappedLine) An unwrapped line.
  """
  bracket_level = 0
  for index, token in enumerate(uwline.tokens):
    if index and not bracket_level:
      pytree_utils.SetNodeAnnotation(token.node,
                                     pytree_utils.Annotation.SPLIT_PENALTY,
                                     split_penalty.UNBREAKABLE)
    if token.value in pytree_utils.OPENING_BRACKETS:
      bracket_level += 1
    elif token.value in pytree_utils.CLOSING_BRACKETS:
      bracket_level -= 1
Exemple #22
0
 def _SetNumNewlines(self, node, num_newlines):
     pytree_utils.SetNodeAnnotation(node, pytree_utils.Annotation.NEWLINES,
                                    num_newlines)
Exemple #23
0
  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 .
      self._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.
            self._SetStronglyConnected(prev_trailer.children[-1])
          self._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:
            self._SetStronglyConnected(_FirstChildNode(trailer.children[1]))

          last_child_node = _LastChildNode(trailer)
          if last_child_node.value.strip().startswith('#'):
            last_child_node = last_child_node.prev_sibling
          if not style.Get('DEDENT_CLOSING_BRACKETS'):
            if _LastChildNode(last_child_node.prev_sibling).value != ',':
              if last_child_node.value == ']':
                self._SetUnbreakable(last_child_node)
              else:
                pytree_utils.SetNodeAnnotation(
                    last_child_node, pytree_utils.Annotation.SPLIT_PENALTY,
                    VERY_STRONGLY_CONNECTED)

          if _FirstChildNode(trailer).lineno == last_child_node.lineno:
            # If the trailer was originally on one line, then try to keep it
            # like that.
            self._SetExpressionPenalty(trailer, CONTIGUOUS_LIST)
        else:
          # If the trailer's children are '()', then make it a strongly
          # connected region.  It's sometimes necessary, though undesirable, to
          # split the two.
          self._SetStronglyConnected(trailer.children[-1])

    # If the original source has a "builder" style calls, then we should allow
    # the reformatter to retain that.
    _AllowBuilderStyleCalls(node)
Exemple #24
0
def _CalculateNumberOfNewlines(first_token, indent_depth, prev_uwline,
                               final_lines):
    """Calculate the number of newlines we need to add.

  Arguments:
    first_token: (format_token.FormatToken) The first token in the unwrapped
      line.
    indent_depth: (int) The line's indentation depth.
    prev_uwline: (list of unwrapped_line.UnwrappedLine) The unwrapped line
      previous to this line.
    final_lines: (list of unwrapped_line.UnwrappedLine) The unwrapped lines
      that have already been processed.

  Returns:
    The number of newlines needed before the first token.
  """
    # TODO(morbo): Special handling for imports.
    # TODO(morbo): Create a knob that can tune these.
    if prev_uwline is None:
        # The first line in the file. Don't add blank lines.
        # FIXME(morbo): Is this correct?
        if first_token.newlines is not None:
            pytree_utils.SetNodeAnnotation(first_token.node,
                                           pytree_utils.Annotation.NEWLINES,
                                           None)
        return 0

    if first_token.is_docstring:
        if (prev_uwline.first.value == 'class'
                and style.Get('BLANK_LINE_BEFORE_CLASS_DOCSTRING')):
            # Enforce a blank line before a class's docstring.
            return ONE_BLANK_LINE
        elif (prev_uwline.first.value.startswith('#')
              and style.Get('BLANK_LINE_BEFORE_MODULE_DOCSTRING')):
            # Enforce a blank line before a module's docstring.
            return ONE_BLANK_LINE
        # The docstring shouldn't have a newline before it.
        return NO_BLANK_LINES

    prev_last_token = prev_uwline.last
    if prev_last_token.is_docstring:
        if (not indent_depth
                and first_token.value in {'class', 'def', 'async'}):
            # Separate a class or function from the module-level docstring with
            # appropriate number of blank lines.
            return 1 + style.Get('BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION')
        if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token,
                                           prev_last_token):
            return NO_BLANK_LINES
        else:
            return ONE_BLANK_LINE

    if first_token.value in {'class', 'def', 'async', '@'}:
        # TODO(morbo): This can go once the blank line calculator is more
        # sophisticated.
        if not indent_depth:
            # This is a top-level class or function.
            is_inline_comment = prev_last_token.whitespace_prefix.count(
                '\n') == 0
            if (not prev_uwline.disable and prev_last_token.is_comment
                    and not is_inline_comment):
                # This token follows a non-inline comment.
                if _NoBlankLinesBeforeCurrentToken(prev_last_token.value,
                                                   first_token,
                                                   prev_last_token):
                    # Assume that the comment is "attached" to the current line.
                    # Therefore, we want two blank lines before the comment.
                    index = len(final_lines) - 1
                    while index > 0:
                        if not final_lines[index - 1].is_comment:
                            break
                        index -= 1
                    if final_lines[index - 1].first.value == '@':
                        final_lines[index].first.AdjustNewlinesBefore(
                            NO_BLANK_LINES)
                    else:
                        prev_last_token.AdjustNewlinesBefore(1 + style.Get(
                            'BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION'))
                    if first_token.newlines is not None:
                        pytree_utils.SetNodeAnnotation(
                            first_token.node, pytree_utils.Annotation.NEWLINES,
                            None)
                    return NO_BLANK_LINES
        elif _IsClassOrDef(prev_uwline):
            if not style.Get('BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'):
                pytree_utils.SetNodeAnnotation(
                    first_token.node, pytree_utils.Annotation.NEWLINES, None)
                return NO_BLANK_LINES

    # Calculate how many newlines were between the original lines. We want to
    # retain that formatting if it doesn't violate one of the style guide rules.
    if first_token.is_comment:
        first_token_lineno = first_token.lineno - first_token.value.count('\n')
    else:
        first_token_lineno = first_token.lineno

    prev_last_token_lineno = prev_last_token.lineno
    if prev_last_token.is_multiline_string:
        prev_last_token_lineno += prev_last_token.value.count('\n')

    if first_token_lineno - prev_last_token_lineno > 1:
        return ONE_BLANK_LINE

    return NO_BLANK_LINES
Exemple #25
0
 def testSetWhenNone(self):
   pytree_utils.SetNodeAnnotation(self._leaf, _FOO, 20)
   self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO), 20)
Exemple #26
0
 def testSetAgain(self):
   pytree_utils.SetNodeAnnotation(self._leaf, _FOO, 20)
   self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO), 20)
   pytree_utils.SetNodeAnnotation(self._leaf, _FOO, 30)
   self.assertEqual(pytree_utils.GetNodeAnnotation(self._leaf, _FOO), 30)
Exemple #27
0
 def testSetOnNode(self):
   pytree_utils.SetNodeAnnotation(self._node, _FOO, 20)
   self.assertEqual(pytree_utils.GetNodeAnnotation(self._node, _FOO), 20)
Exemple #28
0
def _SetMustSplitOnFirstLeaf(node):
    """Set the "must split" annotation on the first leaf node."""
    pytree_utils.SetNodeAnnotation(pytree_utils.FirstLeafNode(node),
                                   pytree_utils.Annotation.MUST_SPLIT, True)
Exemple #29
0
def _CalculateNumberOfNewlines(first_token, indent_depth, prev_uwline):
  """Calculate the number of newlines we need to add.

  Arguments:
    first_token: (format_token.FormatToken) The first token in the unwrapped
      line.
    indent_depth: (int) The line's indentation depth.
    prev_uwline: (list of unwrapped_line.UnwrappedLine) The unwrapped line
      previous to this line.

  Returns:
    The number of newlines needed before the first token.
  """
  # TODO(morbo): Special handling for imports.
  # TODO(morbo): Create a knob that can tune these.
  if prev_uwline is None:
    # The first line in the file. Don't add blank lines.
    # FIXME(morbo): Is this correct?
    if first_token.newlines is not None:
      pytree_utils.SetNodeAnnotation(first_token.GetPytreeNode(),
                                     pytree_utils.Annotation.NEWLINES, None)
    return 0

  if first_token.is_docstring:
    # The docstring shouldn't have a newline before it.
    # TODO(morbo): Add a knob to adjust this.
    return NO_BLANK_LINES

  prev_last_token = prev_uwline.last
  if prev_last_token.is_docstring:
    if not indent_depth and first_token.value in {'class', 'def'}:
      # Separate a class or function from the module-level docstring with two
      # blank lines.
      return TWO_BLANK_LINES
    if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token,
                                       prev_last_token):
      return NO_BLANK_LINES
    else:
      return ONE_BLANK_LINE

  if first_token.value in {'class', 'def', '@'}:
    # TODO(morbo): This can go once the blank line calculator is more
    # sophisticated.
    if not indent_depth:
      # This is a top-level class or function.
      is_inline_comment = prev_last_token.whitespace_prefix.count('\n') == 0
      if prev_last_token.is_comment and not is_inline_comment:
        # This token follows a non-inline comment.
        if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token,
                                           prev_last_token):
          # Assume that the comment is "attached" to the current line.
          # Therefore, we want two blank lines before the comment.
          prev_last_token.AdjustNewlinesBefore(TWO_BLANK_LINES)
          if first_token.newlines is not None:
            pytree_utils.SetNodeAnnotation(first_token.GetPytreeNode(),
                                           pytree_utils.Annotation.NEWLINES,
                                           None)
          return NO_BLANK_LINES
    elif prev_uwline.first.value in {'class', 'def'}:
      if not style.Get('BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'):
        pytree_utils.SetNodeAnnotation(first_token.node,
                                       pytree_utils.Annotation.NEWLINES, None)
        return NO_BLANK_LINES

  # Calculate how many newlines were between the original lines. We want to
  # retain that formatting if it doesn't violate one of the style guide rules.
  if first_token.is_comment:
    first_token_lineno = first_token.lineno - first_token.value.count('\n')
  else:
    first_token_lineno = first_token.lineno

  if first_token_lineno - prev_last_token.lineno > 1:
    return ONE_BLANK_LINE

  return NO_BLANK_LINES
Exemple #30
0
def _SetSplitPenalty(node, penalty):
    pytree_utils.SetNodeAnnotation(node, pytree_utils.Annotation.SPLIT_PENALTY,
                                   penalty)