1# Copyright 2015 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"""Calculate the number of blank lines between top-level entities. 15 16Calculates how many blank lines we need between classes, functions, and other 17entities at the same level. 18 19 CalculateBlankLines(): the main function exported by this module. 20 21Annotations: 22 newlines: The number of newlines required before the node. 23""" 24 25from yapf.yapflib import py3compat 26from yapf.yapflib import pytree_utils 27from yapf.yapflib import pytree_visitor 28from yapf.yapflib import style 29 30_NO_BLANK_LINES = 1 31_ONE_BLANK_LINE = 2 32_TWO_BLANK_LINES = 3 33 34_PYTHON_STATEMENTS = frozenset({ 35 'small_stmt', 'expr_stmt', 'print_stmt', 'del_stmt', 'pass_stmt', 36 'break_stmt', 'continue_stmt', 'return_stmt', 'raise_stmt', 'yield_stmt', 37 'import_stmt', 'global_stmt', 'exec_stmt', 'assert_stmt', 'if_stmt', 38 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt', 'nonlocal_stmt', 39 'async_stmt', 'simple_stmt' 40}) 41 42 43def CalculateBlankLines(tree): 44 """Run the blank line calculator visitor over the tree. 45 46 This modifies the tree in place. 47 48 Arguments: 49 tree: the top-level pytree node to annotate with subtypes. 50 """ 51 blank_line_calculator = _BlankLineCalculator() 52 blank_line_calculator.Visit(tree) 53 54 55class _BlankLineCalculator(pytree_visitor.PyTreeVisitor): 56 """_BlankLineCalculator - see file-level docstring for a description.""" 57 58 def __init__(self): 59 self.class_level = 0 60 self.function_level = 0 61 self.last_comment_lineno = 0 62 self.last_was_decorator = False 63 self.last_was_class_or_function = False 64 65 def Visit_simple_stmt(self, node): # pylint: disable=invalid-name 66 self.DefaultNodeVisit(node) 67 if pytree_utils.NodeName(node.children[0]) == 'COMMENT': 68 self.last_comment_lineno = node.children[0].lineno 69 70 def Visit_decorator(self, node): # pylint: disable=invalid-name 71 if (self.last_comment_lineno and 72 self.last_comment_lineno == node.children[0].lineno - 1): 73 self._SetNumNewlines(node.children[0], _NO_BLANK_LINES) 74 else: 75 self._SetNumNewlines(node.children[0], self._GetNumNewlines(node)) 76 for child in node.children: 77 self.Visit(child) 78 self.last_was_decorator = True 79 80 def Visit_classdef(self, node): # pylint: disable=invalid-name 81 self.last_was_class_or_function = False 82 index = self._SetBlankLinesBetweenCommentAndClassFunc(node) 83 self.last_was_decorator = False 84 self.class_level += 1 85 for child in node.children[index:]: 86 self.Visit(child) 87 self.class_level -= 1 88 self.last_was_class_or_function = True 89 90 def Visit_funcdef(self, node): # pylint: disable=invalid-name 91 self.last_was_class_or_function = False 92 index = self._SetBlankLinesBetweenCommentAndClassFunc(node) 93 if _AsyncFunction(node): 94 index = self._SetBlankLinesBetweenCommentAndClassFunc( 95 node.prev_sibling.parent) 96 self._SetNumNewlines(node.children[0], None) 97 else: 98 index = self._SetBlankLinesBetweenCommentAndClassFunc(node) 99 self.last_was_decorator = False 100 self.function_level += 1 101 for child in node.children[index:]: 102 self.Visit(child) 103 self.function_level -= 1 104 self.last_was_class_or_function = True 105 106 def DefaultNodeVisit(self, node): 107 """Override the default visitor for Node. 108 109 This will set the blank lines required if the last entity was a class or 110 function. 111 112 Arguments: 113 node: (pytree.Node) The node to visit. 114 """ 115 if self.last_was_class_or_function: 116 if pytree_utils.NodeName(node) in _PYTHON_STATEMENTS: 117 leaf = pytree_utils.FirstLeafNode(node) 118 self._SetNumNewlines(leaf, self._GetNumNewlines(leaf)) 119 self.last_was_class_or_function = False 120 super(_BlankLineCalculator, self).DefaultNodeVisit(node) 121 122 def _SetBlankLinesBetweenCommentAndClassFunc(self, node): 123 """Set the number of blanks between a comment and class or func definition. 124 125 Class and function definitions have leading comments as children of the 126 classdef and functdef nodes. 127 128 Arguments: 129 node: (pytree.Node) The classdef or funcdef node. 130 131 Returns: 132 The index of the first child past the comment nodes. 133 """ 134 index = 0 135 while pytree_utils.IsCommentStatement(node.children[index]): 136 # Standalone comments are wrapped in a simple_stmt node with the comment 137 # node as its only child. 138 self.Visit(node.children[index].children[0]) 139 if not self.last_was_decorator: 140 self._SetNumNewlines(node.children[index].children[0], _ONE_BLANK_LINE) 141 index += 1 142 if (index and node.children[index].lineno - 143 1 == node.children[index - 1].children[0].lineno): 144 self._SetNumNewlines(node.children[index], _NO_BLANK_LINES) 145 else: 146 if self.last_comment_lineno + 1 == node.children[index].lineno: 147 num_newlines = _NO_BLANK_LINES 148 else: 149 num_newlines = self._GetNumNewlines(node) 150 self._SetNumNewlines(node.children[index], num_newlines) 151 return index 152 153 def _GetNumNewlines(self, node): 154 if self.last_was_decorator: 155 return _NO_BLANK_LINES 156 elif self._IsTopLevel(node): 157 return 1 + style.Get('BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION') 158 return _ONE_BLANK_LINE 159 160 def _SetNumNewlines(self, node, num_newlines): 161 pytree_utils.SetNodeAnnotation(node, pytree_utils.Annotation.NEWLINES, 162 num_newlines) 163 164 def _IsTopLevel(self, node): 165 return (not (self.class_level or self.function_level) and 166 _StartsInZerothColumn(node)) 167 168 169def _StartsInZerothColumn(node): 170 return (pytree_utils.FirstLeafNode(node).column == 0 or 171 (_AsyncFunction(node) and node.prev_sibling.column == 0)) 172 173 174def _AsyncFunction(node): 175 return (py3compat.PY3 and node.prev_sibling and 176 pytree_utils.NodeName(node.prev_sibling) == 'ASYNC') 177