Ejemplo n.º 1
0
    def MustSplit(self):
        """Returns True if the line must split before the next token."""
        current = self.next_token
        previous = current.previous_token

        if current.is_pseudo_paren:
            return False

        if current.must_break_before:
            return True

        if not previous:
            return False

        if self.stack[
                -1].split_before_closing_bracket and current.value in '}]':
            # Split before the closing bracket if we can.
            return current.node_split_penalty != split_penalty.UNBREAKABLE

        ###########################################################################
        # List Splitting
        if (style.Get('DEDENT_CLOSING_BRACKETS')
                or style.Get('SPLIT_BEFORE_FIRST_ARGUMENT')):
            bracket = current if current.ClosesScope() else previous
            if format_token.Subtype.SUBSCRIPT_BRACKET not in bracket.subtypes:
                if bracket.OpensScope():
                    if style.Get('COALESCE_BRACKETS'):
                        if current.OpensScope():
                            # Prefer to keep all opening brackets together.
                            return False

                    if (not _IsLastScopeInLine(bracket)
                            or unwrapped_line.IsSurroundedByBrackets(bracket)):
                        last_token = bracket.matching_bracket
                    else:
                        last_token = _LastTokenInLine(bracket.matching_bracket)

                    if not self._FitsOnLine(bracket, last_token):
                        # Split before the first element if the whole list can't fit on a
                        # single line.
                        self.stack[-1].split_before_closing_bracket = True
                        return True

                elif style.Get(
                        'DEDENT_CLOSING_BRACKETS') and current.ClosesScope():
                    # Split before and dedent the closing bracket.
                    return self.stack[-1].split_before_closing_bracket

        if (current.is_name or current.is_string) and previous.value == ',':
            # If the list has function calls in it and the full list itself cannot
            # fit on the line, then we want to split. Otherwise, we'll get something
            # like this:
            #
            #     X = [
            #         Bar(xxx='some string',
            #             yyy='another long string',
            #             zzz='a third long string'), Bar(
            #                 xxx='some string',
            #                 yyy='another long string',
            #                 zzz='a third long string')
            #     ]
            #
            # or when a string formatting syntax.
            func_call_or_string_format = False
            if current.is_name:
                tok = current.next_token
                while tok and (tok.is_name or tok.value == '.'):
                    tok = tok.next_token
                func_call_or_string_format = tok.value == '('
            elif current.is_string:
                tok = current.next_token
                while tok and tok.is_string:
                    tok = tok.next_token
                func_call_or_string_format = tok.value == '%'
            if func_call_or_string_format:
                open_bracket = unwrapped_line.IsSurroundedByBrackets(current)
                if open_bracket and open_bracket.value in '[{':
                    if not self._FitsOnLine(open_bracket,
                                            open_bracket.matching_bracket):
                        return True

        ###########################################################################
        # Dict/Set Splitting
        if (style.Get('EACH_DICT_ENTRY_ON_SEPARATE_LINE')
                and format_token.Subtype.DICTIONARY_KEY in current.subtypes
                and not current.is_comment):
            # Place each dictionary entry onto its own line.
            if previous.value == '{' and previous.previous_token:
                opening = _GetOpeningBracket(previous.previous_token)
                if (opening and opening.value == '(' and opening.previous_token
                        and opening.previous_token.is_name):
                    # This is a dictionary that's an argument to a function.
                    if self._FitsOnLine(previous, previous.matching_bracket):
                        return False
            return True

        if (style.Get('SPLIT_BEFORE_DICT_SET_GENERATOR') and
                format_token.Subtype.DICT_SET_GENERATOR in current.subtypes):
            # Split before a dict/set generator.
            return True

        if (format_token.Subtype.DICTIONARY_VALUE in current.subtypes
                or (previous.is_pseudo_paren and previous.value == '('
                    and not current.is_comment)):
            # Split before the dictionary value if we can't fit every dictionary
            # entry on its own line.
            if not current.OpensScope():
                opening = _GetOpeningBracket(current)
                if not self._EachDictEntryFitsOnOneLine(opening):
                    return True

        if previous.value == '{':
            # Split if the dict/set cannot fit on one line and ends in a comma.
            closing = previous.matching_bracket
            if (not self._FitsOnLine(previous, closing)
                    and closing.previous_token.value == ','):
                self.stack[-1].split_before_closing_bracket = True
                return True

        ###########################################################################
        # Argument List Splitting
        if (style.Get('SPLIT_BEFORE_NAMED_ASSIGNS') and not current.is_comment
                and format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST
                in current.subtypes):
            if (previous.value not in {'=', ':', '*', '**'}
                    and current.value not in ':=,)'
                    and not _IsFunctionDefinition(previous)):
                # If we're going to split the lines because of named arguments, then we
                # want to split after the opening bracket as well. But not when this is
                # part of a function definition.
                if previous.value == '(':
                    # Make sure we don't split after the opening bracket if the
                    # continuation indent is greater than the opening bracket:
                    #
                    #  a(
                    #      b=1,
                    #      c=2)
                    if (self._FitsOnLine(previous, previous.matching_bracket)
                            and
                            unwrapped_line.IsSurroundedByBrackets(previous)):
                        # An argument to a function is a function call with named
                        # assigns.
                        return False

                    column = self.column - self.stack[-1].last_space
                    return column > style.Get('CONTINUATION_INDENT_WIDTH')

                opening = _GetOpeningBracket(current)
                if opening:
                    arglist_length = (opening.matching_bracket.total_length -
                                      opening.total_length +
                                      self.stack[-1].indent)
                    return arglist_length > self.column_limit

        if style.Get('SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED'):
            # Split before arguments in a function call or definition if the
            # arguments are terminated by a comma.
            opening = _GetOpeningBracket(current)
            if opening and opening.previous_token and opening.previous_token.is_name:
                if previous.value in '(,':
                    if opening.matching_bracket.previous_token.value == ',':
                        return True

        if ((current.is_name or current.value in {'*', '**'})
                and previous.value == ','):
            # If we have a function call within an argument list and it won't fit on
            # the remaining line, but it will fit on a line by itself, then go ahead
            # and split before the call.
            opening = _GetOpeningBracket(current)
            if (opening and opening.value == '(' and opening.previous_token
                    and (opening.previous_token.is_name
                         or opening.previous_token.value in {'*', '**'})):
                is_func_call = False
                token = current
                while token:
                    if token.value == '(':
                        is_func_call = True
                        break
                    if (not (token.is_name or token.value in {'*', '**'})
                            and token.value != '.'):
                        break
                    token = token.next_token

                if is_func_call:
                    if not self._FitsOnLine(current, opening.matching_bracket):
                        return True

        pprevious = previous.previous_token
        if (current.is_name and pprevious and pprevious.is_name
                and previous.value == '('):
            if (not self._FitsOnLine(previous, previous.matching_bracket)
                    and _IsFunctionCallWithArguments(current)):
                # There is a function call, with more than 1 argument, where the first
                # argument is itself a function call with arguments.  In this specific
                # case, if we split after the first argument's opening '(', then the
                # formatting will look bad for the rest of the arguments. E.g.:
                #
                #     outer_function_call(inner_function_call(
                #         inner_arg1, inner_arg2),
                #                         outer_arg1, outer_arg2)
                #
                # Instead, enforce a split before that argument to keep things looking
                # good.
                return True

        if (previous.OpensScope() and not current.OpensScope()
                and format_token.Subtype.SUBSCRIPT_BRACKET
                not in previous.subtypes):
            if not current.is_comment:
                if pprevious and not pprevious.is_keyword and not pprevious.is_name:
                    # We want to split if there's a comment in the container.
                    token = current
                    while token != previous.matching_bracket:
                        if token.is_comment:
                            return True
                        token = token.next_token

            if previous.value == '(':
                pptoken = previous.previous_token
                if not pptoken or not pptoken.is_name:
                    # Split after the opening of a tuple if it doesn't fit on the current
                    # line and it's not a function call.
                    if self._FitsOnLine(previous, previous.matching_bracket):
                        return False
                elif not self._FitsOnLine(previous, previous.matching_bracket):
                    if (self.column_limit - self.column) / float(
                            self.column_limit) < 0.3:
                        # Try not to squish all of the arguments off to the right.
                        return current.next_token != previous.matching_bracket
            else:
                # Split after the opening of a container if it doesn't fit on the
                # current line or if it has a comment.
                if not self._FitsOnLine(previous, previous.matching_bracket):
                    return True

        ###########################################################################
        # List Comprehension Splitting
        if (format_token.Subtype.COMP_FOR in current.subtypes
                and format_token.Subtype.COMP_FOR not in previous.subtypes):
            # Split at the beginning of a list comprehension.
            length = _GetLengthOfSubtype(current,
                                         format_token.Subtype.COMP_FOR,
                                         format_token.Subtype.COMP_IF)
            if length + self.column > self.column_limit:
                return True

        if (format_token.Subtype.COMP_IF in current.subtypes
                and format_token.Subtype.COMP_IF not in previous.subtypes):
            # Split at the beginning of an if expression.
            length = _GetLengthOfSubtype(current, format_token.Subtype.COMP_IF)
            if length + self.column > self.column_limit:
                return True

        ###########################################################################
        # Original Formatting Splitting
        # These checks rely upon the original formatting. This is in order to
        # attempt to keep hand-written code in the same condition as it was before.
        # However, this may cause the formatter to fail to be idempotent.
        if (style.Get('SPLIT_BEFORE_BITWISE_OPERATOR')
                and current.value in '&|'
                and previous.lineno < current.lineno):
            # Retain the split before a bitwise operator.
            return True

        if (current.is_comment and
                previous.lineno < current.lineno - current.value.count('\n')):
            # If a comment comes in the middle of an unwrapped line (like an if
            # conditional with comments interspersed), then we want to split if the
            # original comments were on a separate line.
            return True

        return False
Ejemplo n.º 2
0
def _IsArgumentToFunction(token):
    bracket = unwrapped_line.IsSurroundedByBrackets(token)
    if not bracket or bracket.value != '(':
        return False
    previous = bracket.previous_token
    return previous and previous.is_name
Ejemplo n.º 3
0
    def MustSplit(self):
        """Returns True if the line must split before the next token."""
        current = self.next_token
        previous = current.previous_token

        if current.is_pseudo_paren:
            return False

        if current.must_break_before:
            return True

        if not previous:
            return False

        if style.Get(
                'SPLIT_ALL_COMMA_SEPARATED_VALUES') and previous.value == ',':
            return True

        if (style.Get('SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES')
                and previous.value == ','):
            # Avoid breaking in a container that fits in the current line if possible
            opening = _GetOpeningBracket(current)

            # Can't find opening bracket, behave the same way as
            # SPLIT_ALL_COMMA_SEPARATED_VALUES
            if not opening:
                return True

            # If the container doesn't fit in the current line, must split
            return not self._ContainerFitsOnStartLine(opening)

        if (self.stack[-1].split_before_closing_bracket
                and current.value in '}]'
                and style.Get('SPLIT_BEFORE_CLOSING_BRACKET')):
            # Split before the closing bracket if we can.
            if format_token.Subtype.SUBSCRIPT_BRACKET not in current.subtypes:
                return current.node_split_penalty != split_penalty.UNBREAKABLE

        if (current.value == ')' and previous.value == ','
                and not _IsSingleElementTuple(current.matching_bracket)):
            return True

        # Prevent splitting before the first argument in compound statements
        # with the exception of function declarations.
        if (style.Get('SPLIT_BEFORE_FIRST_ARGUMENT')
                and _IsCompoundStatement(self.line.first)
                and not _IsFunctionDef(self.line.first)):
            return False

        ###########################################################################
        # List Splitting
        if (style.Get('DEDENT_CLOSING_BRACKETS')
                or style.Get('SPLIT_BEFORE_FIRST_ARGUMENT')):
            bracket = current if current.ClosesScope() else previous
            if format_token.Subtype.SUBSCRIPT_BRACKET not in bracket.subtypes:
                if bracket.OpensScope():
                    if style.Get('COALESCE_BRACKETS'):
                        if current.OpensScope():
                            # Prefer to keep all opening brackets together.
                            return False

                    if (not _IsLastScopeInLine(bracket)
                            or unwrapped_line.IsSurroundedByBrackets(bracket)):
                        last_token = bracket.matching_bracket
                    else:
                        last_token = _LastTokenInLine(bracket.matching_bracket)

                    if not self._FitsOnLine(bracket, last_token):
                        # Split before the first element if the whole list can't fit on a
                        # single line.
                        self.stack[-1].split_before_closing_bracket = True
                        return True

                elif style.Get(
                        'DEDENT_CLOSING_BRACKETS') and current.ClosesScope():
                    # Split before and dedent the closing bracket.
                    return self.stack[-1].split_before_closing_bracket

        if (style.Get('SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN')
                and current.is_name):
            # An expression that's surrounded by parens gets split after the opening
            # parenthesis.
            def SurroundedByParens(token):
                """Check if it's an expression surrounded by parentheses."""
                while token:
                    if token.value == ',':
                        return False
                    if token.value == ')':
                        return not token.next_token
                    if token.OpensScope():
                        token = token.matching_bracket.next_token
                    else:
                        token = token.next_token
                return False

            if (previous.value == '(' and not previous.is_pseudo_paren
                    and not unwrapped_line.IsSurroundedByBrackets(previous)):
                pptoken = previous.previous_token
                if (pptoken and not pptoken.is_name and not pptoken.is_keyword
                        and SurroundedByParens(current)):
                    return True

        if (current.is_name or current.is_string) and previous.value == ',':
            # If the list has function calls in it and the full list itself cannot
            # fit on the line, then we want to split. Otherwise, we'll get something
            # like this:
            #
            #     X = [
            #         Bar(xxx='some string',
            #             yyy='another long string',
            #             zzz='a third long string'), Bar(
            #                 xxx='some string',
            #                 yyy='another long string',
            #                 zzz='a third long string')
            #     ]
            #
            # or when a string formatting syntax.
            func_call_or_string_format = False
            tok = current.next_token
            if current.is_name:
                while tok and (tok.is_name or tok.value == '.'):
                    tok = tok.next_token
                func_call_or_string_format = tok and tok.value == '('
            elif current.is_string:
                while tok and tok.is_string:
                    tok = tok.next_token
                func_call_or_string_format = tok and tok.value == '%'
            if func_call_or_string_format:
                open_bracket = unwrapped_line.IsSurroundedByBrackets(current)
                if open_bracket:
                    if open_bracket.value in '[{':
                        if not self._FitsOnLine(open_bracket,
                                                open_bracket.matching_bracket):
                            return True
                    elif tok.value == '(':
                        if not self._FitsOnLine(current, tok.matching_bracket):
                            return True

        if (current.OpensScope() and previous.value == ','
                and format_token.Subtype.DICTIONARY_KEY
                not in current.next_token.subtypes):
            # If we have a list of tuples, then we can get a similar look as above. If
            # the full list cannot fit on the line, then we want a split.
            open_bracket = unwrapped_line.IsSurroundedByBrackets(current)
            if (open_bracket and open_bracket.value in '[{'
                    and format_token.Subtype.SUBSCRIPT_BRACKET
                    not in open_bracket.subtypes):
                if not self._FitsOnLine(current, current.matching_bracket):
                    return True

        ###########################################################################
        # Dict/Set Splitting
        if (style.Get('EACH_DICT_ENTRY_ON_SEPARATE_LINE')
                and format_token.Subtype.DICTIONARY_KEY in current.subtypes
                and not current.is_comment):
            # Place each dictionary entry onto its own line.
            if previous.value == '{' and previous.previous_token:
                opening = _GetOpeningBracket(previous.previous_token)
                if (opening and opening.value == '(' and opening.previous_token
                        and opening.previous_token.is_name):
                    # This is a dictionary that's an argument to a function.
                    if (self._FitsOnLine(previous, previous.matching_bracket)
                            and previous.matching_bracket.next_token and
                        (not opening.matching_bracket.next_token
                         or opening.matching_bracket.next_token.value != '.')
                            and _ScopeHasNoCommas(previous)):
                        # Don't split before the key if:
                        #   - The dictionary fits on a line, and
                        #   - The function call isn't part of a builder-style call and
                        #   - The dictionary has one entry and no trailing comma
                        return False
            return True

        if (style.Get('SPLIT_BEFORE_DICT_SET_GENERATOR') and
                format_token.Subtype.DICT_SET_GENERATOR in current.subtypes):
            # Split before a dict/set generator.
            return True

        if (format_token.Subtype.DICTIONARY_VALUE in current.subtypes
                or (previous.is_pseudo_paren and previous.value == '('
                    and not current.is_comment)):
            # Split before the dictionary value if we can't fit every dictionary
            # entry on its own line.
            if not current.OpensScope():
                opening = _GetOpeningBracket(current)
                if not self._EachDictEntryFitsOnOneLine(opening):
                    return style.Get('ALLOW_SPLIT_BEFORE_DICT_VALUE')

        if previous.value == '{':
            # Split if the dict/set cannot fit on one line and ends in a comma.
            closing = previous.matching_bracket
            if (not self._FitsOnLine(previous, closing)
                    and closing.previous_token.value == ','):
                self.stack[-1].split_before_closing_bracket = True
                return True

        ###########################################################################
        # Argument List Splitting
        if (style.Get('SPLIT_BEFORE_NAMED_ASSIGNS') and not current.is_comment
                and format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST
                in current.subtypes):
            if (previous.value not in {'=', ':', '*', '**'}
                    and current.value not in ':=,)'
                    and not _IsFunctionDefinition(previous)):
                # If we're going to split the lines because of named arguments, then we
                # want to split after the opening bracket as well. But not when this is
                # part of a function definition.
                if previous.value == '(':
                    # Make sure we don't split after the opening bracket if the
                    # continuation indent is greater than the opening bracket:
                    #
                    #  a(
                    #      b=1,
                    #      c=2)
                    if (self._FitsOnLine(previous, previous.matching_bracket)
                            and
                            unwrapped_line.IsSurroundedByBrackets(previous)):
                        # An argument to a function is a function call with named
                        # assigns.
                        return False

                    # Don't split if not required
                    if (not style.Get(
                            'SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN')
                            and not style.Get('SPLIT_BEFORE_FIRST_ARGUMENT')):
                        return False

                    column = self.column - self.stack[-1].last_space
                    return column > style.Get('CONTINUATION_INDENT_WIDTH')

                opening = _GetOpeningBracket(current)
                if opening:
                    return not self._ContainerFitsOnStartLine(opening)

        if (current.value not in '{)' and previous.value == '('
                and self._ArgumentListHasDictionaryEntry(current)):
            return True

        if style.Get('SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED'):
            # Split before arguments in a function call or definition if the
            # arguments are terminated by a comma.
            opening = _GetOpeningBracket(current)
            if opening and opening.previous_token and opening.previous_token.is_name:
                if previous.value in '(,':
                    if opening.matching_bracket.previous_token.value == ',':
                        return True

        if ((current.is_name or current.value in {'*', '**'})
                and previous.value == ','):
            # If we have a function call within an argument list and it won't fit on
            # the remaining line, but it will fit on a line by itself, then go ahead
            # and split before the call.
            opening = _GetOpeningBracket(current)
            if (opening and opening.value == '(' and opening.previous_token
                    and (opening.previous_token.is_name
                         or opening.previous_token.value in {'*', '**'})):
                is_func_call = False
                opening = current
                while opening:
                    if opening.value == '(':
                        is_func_call = True
                        break
                    if (not (opening.is_name or opening.value in {'*', '**'})
                            and opening.value != '.'):
                        break
                    opening = opening.next_token

                if is_func_call:
                    if (not self._FitsOnLine(current, opening.matching_bracket)
                            or
                        (opening.matching_bracket.next_token
                         and opening.matching_bracket.next_token.value != ','
                         and
                         not opening.matching_bracket.next_token.ClosesScope())
                        ):
                        return True

        pprevious = previous.previous_token

        # A function call with a dictionary as its first argument may result in
        # unreadable formatting if the dictionary spans multiple lines. The
        # dictionary itself is formatted just fine, but the remaning arguments are
        # indented too far:
        #
        #     function_call({
        #         KEY_1: 'value one',
        #         KEY_2: 'value two',
        #     },
        #                   default=False)
        if (current.value == '{' and previous.value == '(' and pprevious
                and pprevious.is_name):
            dict_end = current.matching_bracket
            next_token = dict_end.next_token
            if next_token.value == ',' and not self._FitsOnLine(
                    current, dict_end):
                return True

        if (current.is_name and pprevious and pprevious.is_name
                and previous.value == '('):

            if (not self._FitsOnLine(previous, previous.matching_bracket)
                    and _IsFunctionCallWithArguments(current)):
                # There is a function call, with more than 1 argument, where the first
                # argument is itself a function call with arguments that does not fit
                # into the line.  In this specific case, if we split after the first
                # argument's opening '(', then the formatting will look bad for the
                # rest of the arguments. E.g.:
                #
                #     outer_function_call(inner_function_call(
                #         inner_arg1, inner_arg2),
                #                         outer_arg1, outer_arg2)
                #
                # Instead, enforce a split before that argument to keep things looking
                # good.
                if (style.Get('SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN')
                        or style.Get('SPLIT_BEFORE_FIRST_ARGUMENT')):
                    return True

                opening = _GetOpeningBracket(current)
                if (opening and opening.value == '(' and opening.previous_token
                        and (opening.previous_token.is_name
                             or opening.previous_token.value in {'*', '**'})):
                    is_func_call = False
                    opening = current
                    while opening:
                        if opening.value == '(':
                            is_func_call = True
                            break
                        if (not (opening.is_name
                                 or opening.value in {'*', '**'})
                                and opening.value != '.'):
                            break
                        opening = opening.next_token

                    if is_func_call:
                        if (not self._FitsOnLine(current,
                                                 opening.matching_bracket) or
                            (opening.matching_bracket.next_token and
                             opening.matching_bracket.next_token.value != ','
                             and not opening.matching_bracket.next_token.
                             ClosesScope())):
                            return True

        if (previous.OpensScope() and not current.OpensScope()
                and not current.is_comment
                and format_token.Subtype.SUBSCRIPT_BRACKET
                not in previous.subtypes):
            if pprevious and not pprevious.is_keyword and not pprevious.is_name:
                # We want to split if there's a comment in the container.
                token = current
                while token != previous.matching_bracket:
                    if token.is_comment:
                        return True
                    token = token.next_token
            if previous.value == '(':
                pptoken = previous.previous_token
                if not pptoken or not pptoken.is_name:
                    # Split after the opening of a tuple if it doesn't fit on the current
                    # line and it's not a function call.
                    if self._FitsOnLine(previous, previous.matching_bracket):
                        return False
                elif not self._FitsOnLine(previous, previous.matching_bracket):
                    if len(previous.container_elements) == 1:
                        return False

                    elements = previous.container_elements + [
                        previous.matching_bracket
                    ]
                    i = 1
                    while i < len(elements):
                        if (not elements[i - 1].OpensScope()
                                and not self._FitsOnLine(
                                    elements[i - 1], elements[i])):
                            return True
                        i += 1

                    if (self.column_limit - self.column) / float(
                            self.column_limit) < 0.3:
                        # Try not to squish all of the arguments off to the right.
                        return True
            else:
                # Split after the opening of a container if it doesn't fit on the
                # current line.
                if not self._FitsOnLine(previous, previous.matching_bracket):
                    return True

        ###########################################################################
        # Original Formatting Splitting
        # These checks rely upon the original formatting. This is in order to
        # attempt to keep hand-written code in the same condition as it was before.
        # However, this may cause the formatter to fail to be idempotent.
        if (style.Get('SPLIT_BEFORE_BITWISE_OPERATOR')
                and current.value in '&|'
                and previous.lineno < current.lineno):
            # Retain the split before a bitwise operator.
            return True

        if (current.is_comment and
                previous.lineno < current.lineno - current.value.count('\n')):
            # If a comment comes in the middle of an unwrapped line (like an if
            # conditional with comments interspersed), then we want to split if the
            # original comments were on a separate line.
            return True

        return False
Ejemplo n.º 4
0
    def MustSplit(self):
        """Returns True if the line must split before the next token."""
        current = self.next_token
        previous = current.previous_token
        column_limit = style.Get('COLUMN_LIMIT')

        if current.must_break_before:
            return True

        if previous and style.Get('DEDENT_CLOSING_BRACKETS'):
            bracket = current if current.ClosesScope() else previous
            if format_token.Subtype.SUBSCRIPT_BRACKET not in bracket.subtypes:
                if bracket.OpensScope():
                    if (unwrapped_line.IsSurroundedByBrackets(bracket)
                            or not _IsLastScopeInLine(bracket)):
                        last_token = bracket.matching_bracket
                    else:
                        last_token = _LastTokenInLine(bracket.matching_bracket)

                    length = last_token.total_length - bracket.total_length
                    if length + self.column >= column_limit:
                        return True

                elif current.ClosesScope():
                    opening = bracket.matching_bracket
                    if (unwrapped_line.IsSurroundedByBrackets(opening)
                            or not _IsLastScopeInLine(bracket)
                            or bracket.next_token is None):
                        last_token = bracket
                    else:
                        last_token = _LastTokenInLine(bracket.next_token)

                    length = last_token.total_length - opening.total_length
                    if length + opening.column >= column_limit:
                        return True

        if (self.stack[-1].split_before_closing_bracket and
                # FIXME(morbo): Use the 'matching_bracket' instead of this.
                # FIXME(morbo): Don't forget about tuples!
                current.value in ']}'):
            # Split if we need to split before the closing bracket and the next
            # token is a closing bracket.
            return current.node_split_penalty != split_penalty.UNBREAKABLE

        if not previous:
            return False

        # TODO(morbo): This should be controlled with a knob.
        if (format_token.Subtype.DICTIONARY_KEY in current.subtypes
                and not current.is_comment):
            # Place each dictionary entry on its own line.
            return True

        # TODO(morbo): This should be controlled with a knob.
        if format_token.Subtype.DICT_SET_GENERATOR in current.subtypes:
            return True

        if (style.Get('SPLIT_BEFORE_NAMED_ASSIGNS')
                and format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST
                in current.subtypes):
            if (previous.value not in {'(', '=', '*', '**'}
                    and current.value not in '=,)'):
                opening = _GetOpeningParen(current)
                if opening:
                    arglist_length = (opening.matching_bracket.total_length -
                                      opening.total_length + self.column)
                    return arglist_length > column_limit

        if (previous.value in '{[' and current.lineno != previous.lineno
                and format_token.Subtype.SUBSCRIPT_BRACKET
                not in previous.subtypes):
            return True

        if (previous.value == ':' and _IsDictionaryValue(current)
                and current.lineno != previous.lineno):
            # Retain the split between the dictionary key and value.
            return True

        if (format_token.Subtype.COMP_FOR in current.subtypes
                and format_token.Subtype.COMP_FOR not in previous.subtypes):
            length = _GetLengthOfSubtype(current,
                                         format_token.Subtype.COMP_FOR,
                                         format_token.Subtype.COMP_IF)
            if length + self.column > column_limit:
                return True

        if (format_token.Subtype.COMP_IF in current.subtypes
                and format_token.Subtype.COMP_IF not in previous.subtypes):
            length = _GetLengthOfSubtype(current, format_token.Subtype.COMP_IF)
            if length + self.column > column_limit:
                return True

        previous_previous_token = previous.previous_token
        if (current.name == 'NAME' and previous_previous_token
                and previous_previous_token.name == 'NAME'
                and not previous_previous_token.is_keyword
                and previous.value == '('):
            sibling = previous.node.next_sibling
            if pytree_utils.NodeName(sibling) == 'arglist':
                arglist = previous.node.next_sibling
                if len(arglist.children) > 2:
                    if _IsFunctionCallWithArguments(current):
                        # There is a function call, with more than 1 argument, where
                        # the first argument is itself a function call with arguments.
                        # In this specific case, if we split after the first argument's
                        # opening '(', then the formatting will look bad for the rest
                        # of the arguments. Instead, enforce a split before that
                        # argument to keep things looking good.
                        return True
                    elif (current.OpensScope()
                          and current.matching_bracket.total_length +
                          self.column > column_limit):
                        # There is a data literal that will need to be split and could mess
                        # up the formatting.
                        return True

        if (style.Get('SPLIT_BEFORE_BITWISE_OPERATOR')
                and current.value in '&|'
                and previous.lineno < current.lineno):
            return True

        if (current.is_comment and
                previous.lineno < current.lineno - current.value.count('\n')):
            # If a comment comes in the middle of an unwrapped line (like an if
            # conditional with comments interspersed), then we want to split if the
            # original comments were on a separate line.
            return True

        return False
Ejemplo n.º 5
0
    def MustSplit(self):
        """Returns True if the line must split before the next token."""
        current = self.next_token
        previous = current.previous_token

        if current.is_pseudo_paren:
            return False

        if current.must_break_before:
            return True

        if (previous and (style.Get('DEDENT_CLOSING_BRACKETS')
                          or style.Get('SPLIT_BEFORE_FIRST_ARGUMENT'))):
            bracket = current if current.ClosesScope() else previous
            if format_token.Subtype.SUBSCRIPT_BRACKET not in bracket.subtypes:
                if bracket.OpensScope():
                    if style.Get('COALESCE_BRACKETS'):
                        if current.OpensScope():
                            return False

                    if (not _IsLastScopeInLine(bracket)
                            or unwrapped_line.IsSurroundedByBrackets(bracket)):
                        last_token = bracket.matching_bracket
                    else:
                        last_token = _LastTokenInLine(bracket.matching_bracket)

                    if not self._FitsOnLine(bracket, last_token):
                        self.stack[-1].split_before_closing_bracket = True
                        return True

                elif style.Get(
                        'DEDENT_CLOSING_BRACKETS') and current.ClosesScope():
                    return self.stack[-1].split_before_closing_bracket

        if self.stack[
                -1].split_before_closing_bracket and current.value in '}]':
            # Split if we need to split before the closing bracket.
            return current.node_split_penalty != split_penalty.UNBREAKABLE

        if not previous:
            return False

        # TODO(morbo): This should be controlled with a knob.
        if (format_token.Subtype.DICTIONARY_KEY in current.subtypes
                and not current.is_comment):
            # Place each dictionary entry on its own line.
            if previous.value == '{' and previous.previous_token:
                opening = _GetOpeningBracket(previous.previous_token)
                if (opening and opening.value == '(' and opening.previous_token
                        and opening.previous_token.is_name):
                    # This is a dictionary that's an argument to a function.
                    if self._FitsOnLine(previous, previous.matching_bracket):
                        return False
            return True

        # TODO(morbo): This should be controlled with a knob.
        if format_token.Subtype.DICT_SET_GENERATOR in current.subtypes:
            return True

        if (style.Get('SPLIT_BEFORE_NAMED_ASSIGNS') and not current.is_comment
                and format_token.Subtype.DEFAULT_OR_NAMED_ASSIGN_ARG_LIST
                in current.subtypes):
            if (previous.value not in {'=', ':', '*', '**'}
                    and current.value not in ':=,)'):
                # If we're going to split the lines because of named arguments, then we
                # want to split after the opening bracket as well. But not when this is
                # part of function definition.
                if not _IsFunctionDefinition(previous):
                    # Make sure we don't split after the opening bracket if the
                    # continuation indent is greater than the opening bracket:
                    #
                    #  a(
                    #      b=1,
                    #      c=2)
                    indent_amt = self.stack[-1].indent * style.Get(
                        'INDENT_WIDTH')
                    pptoken = previous.previous_token
                    opening_column = len(
                        pptoken.value) if pptoken else 0 - indent_amt - 1
                    if previous.value == '(':
                        return opening_column >= style.Get(
                            'CONTINUATION_INDENT_WIDTH')
                    opening = _GetOpeningBracket(current)
                    if opening:
                        arglist_length = (
                            opening.matching_bracket.total_length -
                            opening.total_length + self.stack[-1].indent)
                        return arglist_length > self.column_limit

        if style.Get('SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED'):
            # Split before arguments in a function call or definition if the
            # arguments are terminated by a comma.
            opening = _GetOpeningBracket(current)
            if opening and opening.previous_token and opening.previous_token.is_name:
                if previous.value in '(,':
                    if opening.matching_bracket.previous_token.value == ',':
                        return True

        if (format_token.Subtype.DICTIONARY_VALUE in current.subtypes
                or (previous.is_pseudo_paren and previous.value == '(')):
            if not current.OpensScope():
                opening = _GetOpeningBracket(current)
                if previous.is_pseudo_paren:
                    # Split before the dictionary value if we can't fit the whole
                    # dictionary on one line.
                    if not self._EachDictEntryFitsOnOneLine(opening):
                        return True

        if (previous.OpensScope() and not current.OpensScope()
                and format_token.Subtype.SUBSCRIPT_BRACKET
                not in previous.subtypes):

            if not current.is_comment:
                pprevious = previous.previous_token
                if pprevious and not pprevious.is_keyword and not pprevious.is_name:
                    # We want to split if there's a comment in the container.
                    token = current
                    while token != previous.matching_bracket:
                        if token.is_comment:
                            return True
                        token = token.next_token

            if previous.value == '(':
                pptoken = previous.previous_token
                if not pptoken or not pptoken.is_name:
                    # Split after the opening of a tuple if it doesn't fit on the current
                    # line and it's not a function call.
                    if self._FitsOnLine(previous, previous.matching_bracket):
                        return False
            else:
                # Split after the opening of a container if it doesn't fit on the
                # current line or if it has a comment.
                if not self._FitsOnLine(previous, previous.matching_bracket):
                    return True

        if previous.value == '{':
            closing = previous.matching_bracket
            if (not self._FitsOnLine(previous, closing)
                    and closing.previous_token.value == ','):
                self.stack[-1].split_before_closing_bracket = True
                return True

        if (format_token.Subtype.COMP_FOR in current.subtypes
                and format_token.Subtype.COMP_FOR not in previous.subtypes):
            # Split at the beginning of a list comprehension.
            length = _GetLengthOfSubtype(current,
                                         format_token.Subtype.COMP_FOR,
                                         format_token.Subtype.COMP_IF)
            if length + self.column > self.column_limit:
                return True

        if (format_token.Subtype.COMP_IF in current.subtypes
                and format_token.Subtype.COMP_IF not in previous.subtypes):
            # Split at the beginning of an if expression.
            length = _GetLengthOfSubtype(current, format_token.Subtype.COMP_IF)
            if length + self.column > self.column_limit:
                return True

        previous_previous_token = previous.previous_token
        if (current.is_name and previous_previous_token
                and previous_previous_token.is_name and previous.value == '('):
            if not self._FitsOnLine(previous, previous.matching_bracket):
                if _IsFunctionCallWithArguments(current):
                    # There is a function call, with more than 1 argument, where
                    # the first argument is itself a function call with arguments.
                    # In this specific case, if we split after the first argument's
                    # opening '(', then the formatting will look bad for the rest
                    # of the arguments. Instead, enforce a split before that
                    # argument to keep things looking good.
                    return True
                elif current.OpensScope():
                    if not self._FitsOnLine(current, current.matching_bracket):
                        # There is a data literal that will need to be split and could mess
                        # up the formatting.
                        return True

        if (style.Get('SPLIT_BEFORE_BITWISE_OPERATOR')
                and current.value in '&|'
                and previous.lineno < current.lineno):
            # Retain the split before a bitwise operator.
            return True

        if (current.is_comment and
                previous.lineno < current.lineno - current.value.count('\n')):
            # If a comment comes in the middle of an unwrapped line (like an if
            # conditional with comments interspersed), then we want to split if the
            # original comments were on a separate line.
            return True

        if current.is_name and previous.value == ',':
            # If we have a function call within an argument list and it won't fit on
            # the remaining line, but it will fit on a line by itself, then go ahead
            # and split before the call.
            opening = _GetOpeningBracket(current)
            if (opening and opening.value == '(' and opening.previous_token
                    and opening.previous_token.is_name):
                is_func_call = False
                token = current
                while token:
                    if token.value == '(':
                        is_func_call = True
                        break
                    if not token.is_name and token.value != '.':
                        break
                    token = token.next_token

                if is_func_call:
                    if not self._FitsOnLine(current, opening.matching_bracket):
                        return True

        return False