Skip to content

Commit

Permalink
Add a new knob to allow logical operators to split
Browse files Browse the repository at this point in the history
This comes from the issue
google#844
where logical operators are bin-packed if the line is too long instead
of wrapping nicely one to each line
  • Loading branch information
jesse-sony committed Aug 25, 2023
1 parent e5328a0 commit 47c1d33
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 15 deletions.
51 changes: 51 additions & 0 deletions yapf/yapflib/format_decision_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,37 @@ def SurroundedByParens(token):
if not self._FitsOnLine(previous, previous.matching_bracket):
return True

###########################################################################
# Logical Operator Splitting
if style.Get('SPLIT_ALL_LOGICAL_OPERATORS_IF_ANY_SPLIT'):
split_before = style.Get("SPLIT_BEFORE_LOGICAL_OPERATOR")
check_token = current if split_before else current.previous_token
if (check_token and check_token.name == "NAME" and check_token.value in logical_line._LOGICAL_OPERATORS):
opening = _GetOpeningBracket(check_token)
if opening:
ending = opening.matching_bracket
length = ending.total_length - opening.total_length
length += self.stack[-1].indent
# If we're keeping it on a single line, but the next token is also
# a logical operator then we have to consider that as part of the
# length because we might wrap after it
next_token = ending.next_token
prev_token = opening.previous_token
if split_before:
if prev_token:
clause_start = _PrevLogicalClause(prev_token)
length += opening.total_length - clause_start.total_length
else:
if next_token:
clause_end = _NextLogicalClause(next_token)
length += clause_end.total_length - ending.total_length
else:
end_token = _LastTokenInLine(check_token)
length = end_token.total_length + self.stack[-1].indent

if length >= self.column_limit:
return True

###########################################################################
# Original Formatting Splitting
# These checks rely upon the original formatting. This is in order to
Expand Down Expand Up @@ -1159,6 +1190,26 @@ def _LastTokenInLine(current):
return current


def _NextLogicalClause(token):
""" Get the start of the next logical clause or the last token in the line"""
while token:
if token in logical_line._LOGICAL_OPERATORS:
return token
if not token.next_token:
return token
token = token.next_token


def _PrevLogicalClause(token):
""" Get the start of the previous logical clause or the first token"""
while token:
if token.value in logical_line._LOGICAL_OPERATORS:
return token
if not token.previous_token:
return token
token = token.previous_token


def _IsFunctionDefinition(current):
prev = current.previous_token
return current.value == '(' and prev and subtypes.FUNC_DEF in prev.subtypes
Expand Down
49 changes: 34 additions & 15 deletions yapf/yapflib/logical_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,21 +610,40 @@ def _SplitPenalty(prev_token, cur_token):
if pval == 'not':
return split_penalty.UNBREAKABLE

if cur_token.node_split_penalty > 0:
return cur_token.node_split_penalty

if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
# Prefer to split before 'and' and 'or'.
if pval in _LOGICAL_OPERATORS:
return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
if cval in _LOGICAL_OPERATORS:
return 0
else:
# Prefer to split after 'and' and 'or'.
if pval in _LOGICAL_OPERATORS:
return 0
if cval in _LOGICAL_OPERATORS:
return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
node_split_penalty = cur_token.node_split_penalty

logical_splitting = style.Get('SPLIT_ALL_LOGICAL_OPERATORS_IF_ANY_SPLIT')

if logical_splitting:
if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
# Prefer to split before 'and' and 'or'.
if pval in _LOGICAL_OPERATORS:
return max(node_split_penalty, style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR'))
if cval in _LOGICAL_OPERATORS:
return max(node_split_penalty, 0)
else:
# Prefer to split after 'and' and 'or'.
if pval in _LOGICAL_OPERATORS:
return max(node_split_penalty, 0)
if cval in _LOGICAL_OPERATORS:
return max(node_split_penalty, style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR'))

if node_split_penalty > 0:
return node_split_penalty

if not logical_splitting:
if style.Get('SPLIT_BEFORE_LOGICAL_OPERATOR'):
# Prefer to split before 'and' and 'or'.
if pval in _LOGICAL_OPERATORS:
return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')
if cval in _LOGICAL_OPERATORS:
return 0
else:
# Prefer to split after 'and' and 'or'.
if pval in _LOGICAL_OPERATORS:
return 0
if cval in _LOGICAL_OPERATORS:
return style.Get('SPLIT_PENALTY_LOGICAL_OPERATOR')

if style.Get('SPLIT_BEFORE_BITWISE_OPERATOR'):
# Prefer to split before '&', '|', and '^'.
Expand Down
8 changes: 8 additions & 0 deletions yapf/yapflib/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ def method():
SPLIT_ALL_COMMA_SEPARATED_VALUES=textwrap.dedent("""\
Split before arguments.
"""),
SPLIT_ALL_LOGICAL_OPERATORS_IF_ANY_SPLIT=textwrap.dedent("""\
If a line that contains logical operators needs to be split, split on all
the logical operators in that line. This will treat logical operators
in sub-clauses defined by parentheses as a discrete element that will
ignore wrapping unless the entire clause is longer than the column width.
"""),
SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES=textwrap.dedent("""\
Split before arguments, but do not split all subexpressions recursively
(unless needed).
Expand Down Expand Up @@ -497,6 +503,7 @@ def CreatePEP8Style():
SPACES_AROUND_TUPLE_DELIMITERS=False,
SPACES_BEFORE_COMMENT=2,
SPLIT_ALL_COMMA_SEPARATED_VALUES=False,
SPLIT_ALL_LOGICAL_OPERATORS_IF_ANY_SPLIT=False,
SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES=False,
SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=False,
SPLIT_BEFORE_ARITHMETIC_OPERATOR=False,
Expand Down Expand Up @@ -685,6 +692,7 @@ def _IntOrIntListConverter(s):
SPACES_AROUND_TUPLE_DELIMITERS=_BoolConverter,
SPACES_BEFORE_COMMENT=_IntOrIntListConverter,
SPLIT_ALL_COMMA_SEPARATED_VALUES=_BoolConverter,
SPLIT_ALL_LOGICAL_OPERATORS_IF_ANY_SPLIT=_BoolConverter,
SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES=_BoolConverter,
SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=_BoolConverter,
SPLIT_BEFORE_ARITHMETIC_OPERATOR=_BoolConverter,
Expand Down

0 comments on commit 47c1d33

Please sign in to comment.