1 # Copyright 2017 Google Inc. All Rights Reserved.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """Represents the state of Python objects being formatted.
16 Objects (e.g., list comprehensions, dictionaries, etc.) have specific
17 requirements on how they're formatted. These state objects keep track of these
21 from functools
import lru_cache
23 from yapf
.yapflib
import style
24 from yapf
.yapflib
import subtypes
27 class ComprehensionState(object):
28 """Maintains the state of list comprehension formatting decisions.
30 A stack of ComprehensionState objects are kept to ensure that list
31 comprehensions are wrapped with well-defined rules.
34 expr_token: The first token in the comprehension.
35 for_token: The first 'for' token of the comprehension.
36 opening_bracket: The opening bracket of the list comprehension.
37 closing_bracket: The closing bracket of the list comprehension.
38 has_split_at_for: Whether there is a newline immediately before the
40 has_interior_split: Whether there is a newline within the comprehension.
41 That is, a split somewhere after expr_token or before closing_bracket.
44 def __init__(self
, expr_token
):
45 self
.expr_token
= expr_token
47 self
.has_split_at_for
= False
48 self
.has_interior_split
= False
50 def HasTrivialExpr(self
):
51 """Returns whether the comp_expr is "trivial" i.e. is a single token."""
52 return self
.expr_token
.next_token
.value
== 'for'
55 def opening_bracket(self
):
56 return self
.expr_token
.previous_token
59 def closing_bracket(self
):
60 return self
.opening_bracket
.matching_bracket
63 clone
= ComprehensionState(self
.expr_token
)
64 clone
.for_token
= self
.for_token
65 clone
.has_split_at_for
= self
.has_split_at_for
66 clone
.has_interior_split
= self
.has_interior_split
70 return ('[opening_bracket::%s, for_token::%s, has_split_at_for::%s,'
71 ' has_interior_split::%s, has_trivial_expr::%s]' %
72 (self
.opening_bracket
, self
.for_token
, self
.has_split_at_for
,
73 self
.has_interior_split
, self
.HasTrivialExpr()))
75 def __eq__(self
, other
):
76 return hash(self
) == hash(other
)
78 def __ne__(self
, other
):
79 return not self
== other
81 def __hash__(self
, *args
, **kwargs
):
82 return hash((self
.expr_token
, self
.for_token
, self
.has_split_at_for
,
83 self
.has_interior_split
))
86 class ParameterListState(object):
87 """Maintains the state of function parameter list formatting decisions.
90 opening_bracket: The opening bracket of the parameter list.
91 closing_bracket: The closing bracket of the parameter list.
92 has_typed_return: True if the function definition has a typed return.
93 ends_in_comma: True if the parameter list ends in a comma.
94 last_token: Returns the last token of the function declaration.
95 has_default_values: True if the parameters have default values.
96 has_split_before_first_param: Whether there is a newline before the first
98 opening_column: The position of the opening parameter before a newline.
99 parameters: A list of parameter objects (Parameter).
100 split_before_closing_bracket: Split before the closing bracket. Sometimes
101 needed if the indentation would collide.
104 def __init__(self
, opening_bracket
, newline
, opening_column
):
105 self
.opening_bracket
= opening_bracket
106 self
.has_split_before_first_param
= newline
107 self
.opening_column
= opening_column
108 self
.parameters
= opening_bracket
.parameters
109 self
.split_before_closing_bracket
= False
112 def closing_bracket(self
):
113 return self
.opening_bracket
.matching_bracket
116 def has_typed_return(self
):
117 return self
.closing_bracket
.next_token
.value
== '->'
121 def has_default_values(self
):
122 return any(param
.has_default_value
for param
in self
.parameters
)
126 def ends_in_comma(self
):
127 if not self
.parameters
:
129 return self
.parameters
[-1].last_token
.next_token
.value
== ','
133 def last_token(self
):
134 token
= self
.opening_bracket
.matching_bracket
135 while not token
.is_comment
and token
.next_token
:
136 token
= token
.next_token
140 def LastParamFitsOnLine(self
, indent
):
141 """Return true if the last parameter fits on a single line."""
142 if not self
.has_typed_return
:
144 if not self
.parameters
:
146 total_length
= self
.last_token
.total_length
147 last_param
= self
.parameters
[-1].first_token
148 total_length
-= last_param
.total_length
- len(last_param
.value
)
149 return total_length
+ indent
<= style
.Get('COLUMN_LIMIT')
152 def SplitBeforeClosingBracket(self
, indent
):
153 """Return true if there's a split before the closing bracket."""
154 if style
.Get('DEDENT_CLOSING_BRACKETS'):
156 if self
.ends_in_comma
:
158 if not self
.parameters
:
160 total_length
= self
.last_token
.total_length
161 last_param
= self
.parameters
[-1].first_token
162 total_length
-= last_param
.total_length
- len(last_param
.value
)
163 return total_length
+ indent
> style
.Get('COLUMN_LIMIT')
166 clone
= ParameterListState(self
.opening_bracket
,
167 self
.has_split_before_first_param
,
169 clone
.split_before_closing_bracket
= self
.split_before_closing_bracket
170 clone
.parameters
= [param
.Clone() for param
in self
.parameters
]
174 return ('[opening_bracket::%s, has_split_before_first_param::%s, '
175 'opening_column::%d]' %
176 (self
.opening_bracket
, self
.has_split_before_first_param
,
177 self
.opening_column
))
179 def __eq__(self
, other
):
180 return hash(self
) == hash(other
)
182 def __ne__(self
, other
):
183 return not self
== other
185 def __hash__(self
, *args
, **kwargs
):
187 (self
.opening_bracket
, self
.has_split_before_first_param
,
188 self
.opening_column
, (hash(param
) for param
in self
.parameters
)))
191 class Parameter(object):
192 """A parameter in a parameter list.
195 first_token: (format_token.FormatToken) First token of parameter.
196 last_token: (format_token.FormatToken) Last token of parameter.
197 has_default_value: (boolean) True if the parameter has a default value
200 def __init__(self
, first_token
, last_token
):
201 self
.first_token
= first_token
202 self
.last_token
= last_token
206 def has_default_value(self
):
207 """Returns true if the parameter has a default value."""
208 tok
= self
.first_token
209 while tok
!= self
.last_token
:
210 if subtypes
.DEFAULT_OR_NAMED_ASSIGN
in tok
.subtypes
:
212 tok
= tok
.matching_bracket
if tok
.OpensScope() else tok
.next_token
216 return Parameter(self
.first_token
, self
.last_token
)
219 return '[first_token::%s, last_token:%s]' % (self
.first_token
,
222 def __eq__(self
, other
):
223 return hash(self
) == hash(other
)
225 def __ne__(self
, other
):
226 return not self
== other
228 def __hash__(self
, *args
, **kwargs
):
229 return hash((self
.first_token
, self
.last_token
))