object_state.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. # Copyright 2017 Google Inc. All Rights Reserved.
  2. #
  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
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  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.
  15. Objects (e.g., list comprehensions, dictionaries, etc.) have specific
  16. requirements on how they're formatted. These state objects keep track of these
  17. requirements.
  18. """
  19. from functools import lru_cache
  20. from yapf.yapflib import style
  21. from yapf.yapflib import subtypes
  22. class ComprehensionState(object):
  23. """Maintains the state of list comprehension formatting decisions.
  24. A stack of ComprehensionState objects are kept to ensure that list
  25. comprehensions are wrapped with well-defined rules.
  26. Attributes:
  27. expr_token: The first token in the comprehension.
  28. for_token: The first 'for' token of the comprehension.
  29. opening_bracket: The opening bracket of the list comprehension.
  30. closing_bracket: The closing bracket of the list comprehension.
  31. has_split_at_for: Whether there is a newline immediately before the
  32. for_token.
  33. has_interior_split: Whether there is a newline within the comprehension.
  34. That is, a split somewhere after expr_token or before closing_bracket.
  35. """
  36. def __init__(self, expr_token):
  37. self.expr_token = expr_token
  38. self.for_token = None
  39. self.has_split_at_for = False
  40. self.has_interior_split = False
  41. def HasTrivialExpr(self):
  42. """Returns whether the comp_expr is "trivial" i.e. is a single token."""
  43. return self.expr_token.next_token.value == 'for'
  44. @property
  45. def opening_bracket(self):
  46. return self.expr_token.previous_token
  47. @property
  48. def closing_bracket(self):
  49. return self.opening_bracket.matching_bracket
  50. def Clone(self):
  51. clone = ComprehensionState(self.expr_token)
  52. clone.for_token = self.for_token
  53. clone.has_split_at_for = self.has_split_at_for
  54. clone.has_interior_split = self.has_interior_split
  55. return clone
  56. def __repr__(self):
  57. return ('[opening_bracket::%s, for_token::%s, has_split_at_for::%s,'
  58. ' has_interior_split::%s, has_trivial_expr::%s]' %
  59. (self.opening_bracket, self.for_token, self.has_split_at_for,
  60. self.has_interior_split, self.HasTrivialExpr()))
  61. def __eq__(self, other):
  62. return hash(self) == hash(other)
  63. def __ne__(self, other):
  64. return not self == other
  65. def __hash__(self, *args, **kwargs):
  66. return hash((self.expr_token, self.for_token, self.has_split_at_for,
  67. self.has_interior_split))
  68. class ParameterListState(object):
  69. """Maintains the state of function parameter list formatting decisions.
  70. Attributes:
  71. opening_bracket: The opening bracket of the parameter list.
  72. closing_bracket: The closing bracket of the parameter list.
  73. has_typed_return: True if the function definition has a typed return.
  74. ends_in_comma: True if the parameter list ends in a comma.
  75. last_token: Returns the last token of the function declaration.
  76. has_default_values: True if the parameters have default values.
  77. has_split_before_first_param: Whether there is a newline before the first
  78. parameter.
  79. opening_column: The position of the opening parameter before a newline.
  80. parameters: A list of parameter objects (Parameter).
  81. split_before_closing_bracket: Split before the closing bracket. Sometimes
  82. needed if the indentation would collide.
  83. """
  84. def __init__(self, opening_bracket, newline, opening_column):
  85. self.opening_bracket = opening_bracket
  86. self.has_split_before_first_param = newline
  87. self.opening_column = opening_column
  88. self.parameters = opening_bracket.parameters
  89. self.split_before_closing_bracket = False
  90. @property
  91. def closing_bracket(self):
  92. return self.opening_bracket.matching_bracket
  93. @property
  94. def has_typed_return(self):
  95. return self.closing_bracket.next_token.value == '->'
  96. @property
  97. @lru_cache()
  98. def has_default_values(self):
  99. return any(param.has_default_value for param in self.parameters)
  100. @property
  101. @lru_cache()
  102. def ends_in_comma(self):
  103. if not self.parameters:
  104. return False
  105. return self.parameters[-1].last_token.next_token.value == ','
  106. @property
  107. @lru_cache()
  108. def last_token(self):
  109. token = self.opening_bracket.matching_bracket
  110. while not token.is_comment and token.next_token:
  111. token = token.next_token
  112. return token
  113. @lru_cache()
  114. def LastParamFitsOnLine(self, indent):
  115. """Return true if the last parameter fits on a single line."""
  116. if not self.has_typed_return:
  117. return False
  118. if not self.parameters:
  119. return True
  120. total_length = self.last_token.total_length
  121. last_param = self.parameters[-1].first_token
  122. total_length -= last_param.total_length - len(last_param.value)
  123. return total_length + indent <= style.Get('COLUMN_LIMIT')
  124. @lru_cache()
  125. def SplitBeforeClosingBracket(self, indent):
  126. """Return true if there's a split before the closing bracket."""
  127. if style.Get('DEDENT_CLOSING_BRACKETS'):
  128. return True
  129. if self.ends_in_comma:
  130. return True
  131. if not self.parameters:
  132. return False
  133. total_length = self.last_token.total_length
  134. last_param = self.parameters[-1].first_token
  135. total_length -= last_param.total_length - len(last_param.value)
  136. return total_length + indent > style.Get('COLUMN_LIMIT')
  137. def Clone(self):
  138. clone = ParameterListState(self.opening_bracket,
  139. self.has_split_before_first_param,
  140. self.opening_column)
  141. clone.split_before_closing_bracket = self.split_before_closing_bracket
  142. clone.parameters = [param.Clone() for param in self.parameters]
  143. return clone
  144. def __repr__(self):
  145. return ('[opening_bracket::%s, has_split_before_first_param::%s, '
  146. 'opening_column::%d]' %
  147. (self.opening_bracket, self.has_split_before_first_param,
  148. self.opening_column))
  149. def __eq__(self, other):
  150. return hash(self) == hash(other)
  151. def __ne__(self, other):
  152. return not self == other
  153. def __hash__(self, *args, **kwargs):
  154. return hash(
  155. (self.opening_bracket, self.has_split_before_first_param,
  156. self.opening_column, (hash(param) for param in self.parameters)))
  157. class Parameter(object):
  158. """A parameter in a parameter list.
  159. Attributes:
  160. first_token: (format_token.FormatToken) First token of parameter.
  161. last_token: (format_token.FormatToken) Last token of parameter.
  162. has_default_value: (boolean) True if the parameter has a default value
  163. """
  164. def __init__(self, first_token, last_token):
  165. self.first_token = first_token
  166. self.last_token = last_token
  167. @property
  168. @lru_cache()
  169. def has_default_value(self):
  170. """Returns true if the parameter has a default value."""
  171. tok = self.first_token
  172. while tok != self.last_token:
  173. if subtypes.DEFAULT_OR_NAMED_ASSIGN in tok.subtypes:
  174. return True
  175. tok = tok.matching_bracket if tok.OpensScope() else tok.next_token
  176. return False
  177. def Clone(self):
  178. return Parameter(self.first_token, self.last_token)
  179. def __repr__(self):
  180. return '[first_token::%s, last_token:%s]' % (self.first_token,
  181. self.last_token)
  182. def __eq__(self, other):
  183. return hash(self) == hash(other)
  184. def __ne__(self, other):
  185. return not self == other
  186. def __hash__(self, *args, **kwargs):
  187. return hash((self.first_token, self.last_token))