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"""Decide what the format for the code should be. 15 16The `logical_line.LogicalLine`s are now ready to be formatted. LogicalLInes that 17can be merged together are. The best formatting is returned as a string. 18 19 Reformat(): the main function exported by this module. 20""" 21 22from __future__ import unicode_literals 23import collections 24import heapq 25import re 26 27from lib2to3 import pytree 28from lib2to3.pgen2 import token 29 30from yapf.yapflib import format_decision_state 31from yapf.yapflib import format_token 32from yapf.yapflib import line_joiner 33from yapf.yapflib import pytree_utils 34from yapf.yapflib import style 35from yapf.yapflib import verifier 36 37 38def Reformat(llines, verify=False, lines=None): 39 """Reformat the logical lines. 40 41 Arguments: 42 llines: (list of logical_line.LogicalLine) Lines we want to format. 43 verify: (bool) True if reformatted code should be verified for syntax. 44 lines: (set of int) The lines which can be modified or None if there is no 45 line range restriction. 46 47 Returns: 48 A string representing the reformatted code. 49 """ 50 final_lines = [] 51 prev_line = None # The previous line. 52 indent_width = style.Get('INDENT_WIDTH') 53 54 for lline in _SingleOrMergedLines(llines): 55 first_token = lline.first 56 _FormatFirstToken(first_token, lline.depth, prev_line, final_lines) 57 58 indent_amt = indent_width * lline.depth 59 state = format_decision_state.FormatDecisionState(lline, indent_amt) 60 state.MoveStateToNextToken() 61 62 if not lline.disable: 63 if lline.first.is_comment: 64 lline.first.node.value = lline.first.node.value.rstrip() 65 elif lline.last.is_comment: 66 lline.last.node.value = lline.last.node.value.rstrip() 67 if prev_line and prev_line.disable: 68 # Keep the vertical spacing between a disabled and enabled formatting 69 # region. 70 _RetainRequiredVerticalSpacingBetweenTokens(lline.first, prev_line.last, 71 lines) 72 if any(tok.is_comment for tok in lline.tokens): 73 _RetainVerticalSpacingBeforeComments(lline) 74 75 if lline.disable or _LineHasContinuationMarkers(lline): 76 _RetainHorizontalSpacing(lline) 77 _RetainRequiredVerticalSpacing(lline, prev_line, lines) 78 _EmitLineUnformatted(state) 79 80 elif (_LineContainsPylintDisableLineTooLong(lline) or 81 _LineContainsI18n(lline)): 82 # Don't modify vertical spacing, but fix any horizontal spacing issues. 83 _RetainRequiredVerticalSpacing(lline, prev_line, lines) 84 _EmitLineUnformatted(state) 85 86 elif _CanPlaceOnSingleLine(lline) and not any(tok.must_break_before 87 for tok in lline.tokens): 88 # The logical line fits on one line. 89 while state.next_token: 90 state.AddTokenToState(newline=False, dry_run=False) 91 92 elif not _AnalyzeSolutionSpace(state): 93 # Failsafe mode. If there isn't a solution to the line, then just emit 94 # it as is. 95 state = format_decision_state.FormatDecisionState(lline, indent_amt) 96 state.MoveStateToNextToken() 97 _RetainHorizontalSpacing(lline) 98 _RetainRequiredVerticalSpacing(lline, prev_line, None) 99 _EmitLineUnformatted(state) 100 101 final_lines.append(lline) 102 prev_line = lline 103 104 _AlignTrailingComments(final_lines) 105 return _FormatFinalLines(final_lines, verify) 106 107 108def _RetainHorizontalSpacing(line): 109 """Retain all horizontal spacing between tokens.""" 110 for tok in line.tokens: 111 tok.RetainHorizontalSpacing(line.first.column, line.depth) 112 113 114def _RetainRequiredVerticalSpacing(cur_line, prev_line, lines): 115 """Retain all vertical spacing between lines.""" 116 prev_tok = None 117 if prev_line is not None: 118 prev_tok = prev_line.last 119 120 if cur_line.disable: 121 # After the first token we are acting on a single line. So if it is 122 # disabled we must not reformat. 123 lines = set() 124 125 for cur_tok in cur_line.tokens: 126 _RetainRequiredVerticalSpacingBetweenTokens(cur_tok, prev_tok, lines) 127 prev_tok = cur_tok 128 129 130def _RetainRequiredVerticalSpacingBetweenTokens(cur_tok, prev_tok, lines): 131 """Retain vertical spacing between two tokens if not in editable range.""" 132 if prev_tok is None: 133 return 134 135 if prev_tok.is_string: 136 prev_lineno = prev_tok.lineno + prev_tok.value.count('\n') 137 elif prev_tok.is_pseudo: 138 if not prev_tok.previous_token.is_multiline_string: 139 prev_lineno = prev_tok.previous_token.lineno 140 else: 141 prev_lineno = prev_tok.lineno 142 else: 143 prev_lineno = prev_tok.lineno 144 145 if cur_tok.is_comment: 146 cur_lineno = cur_tok.lineno - cur_tok.value.count('\n') 147 else: 148 cur_lineno = cur_tok.lineno 149 150 if not prev_tok.is_comment and prev_tok.value.endswith('\\'): 151 prev_lineno += prev_tok.value.count('\n') 152 153 required_newlines = cur_lineno - prev_lineno 154 if cur_tok.is_comment and not prev_tok.is_comment: 155 # Don't adjust between a comment and non-comment. 156 pass 157 elif lines and lines.intersection(range(prev_lineno, cur_lineno + 1)): 158 desired_newlines = cur_tok.whitespace_prefix.count('\n') 159 whitespace_lines = range(prev_lineno + 1, cur_lineno) 160 deletable_lines = len(lines.intersection(whitespace_lines)) 161 required_newlines = max(required_newlines - deletable_lines, 162 desired_newlines) 163 164 cur_tok.AdjustNewlinesBefore(required_newlines) 165 166 167def _RetainVerticalSpacingBeforeComments(line): 168 """Retain vertical spacing before comments.""" 169 prev_token = None 170 for tok in line.tokens: 171 if tok.is_comment and prev_token: 172 if tok.lineno - tok.value.count('\n') - prev_token.lineno > 1: 173 tok.AdjustNewlinesBefore(ONE_BLANK_LINE) 174 175 prev_token = tok 176 177 178def _EmitLineUnformatted(state): 179 """Emit the line without formatting. 180 181 The line contains code that if reformatted would break a non-syntactic 182 convention. E.g., i18n comments and function calls are tightly bound by 183 convention. Instead, we calculate when / if a newline should occur and honor 184 that. But otherwise the code emitted will be the same as the original code. 185 186 Arguments: 187 state: (format_decision_state.FormatDecisionState) The format decision 188 state. 189 """ 190 while state.next_token: 191 previous_token = state.next_token.previous_token 192 previous_lineno = previous_token.lineno 193 194 if previous_token.is_multiline_string or previous_token.is_string: 195 previous_lineno += previous_token.value.count('\n') 196 197 if previous_token.is_continuation: 198 newline = False 199 else: 200 newline = state.next_token.lineno > previous_lineno 201 202 state.AddTokenToState(newline=newline, dry_run=False) 203 204 205def _LineContainsI18n(line): 206 """Return true if there are i18n comments or function calls in the line. 207 208 I18n comments and pseudo-function calls are closely related. They cannot 209 be moved apart without breaking i18n. 210 211 Arguments: 212 line: (logical_line.LogicalLine) The line currently being formatted. 213 214 Returns: 215 True if the line contains i18n comments or function calls. False otherwise. 216 """ 217 if style.Get('I18N_COMMENT'): 218 for tok in line.tokens: 219 if tok.is_comment and re.match(style.Get('I18N_COMMENT'), tok.value): 220 # Contains an i18n comment. 221 return True 222 223 if style.Get('I18N_FUNCTION_CALL'): 224 length = len(line.tokens) 225 for index in range(length - 1): 226 if (line.tokens[index + 1].value == '(' and 227 line.tokens[index].value in style.Get('I18N_FUNCTION_CALL')): 228 return True 229 return False 230 231 232def _LineContainsPylintDisableLineTooLong(line): 233 """Return true if there is a "pylint: disable=line-too-long" comment.""" 234 return re.search(r'\bpylint:\s+disable=line-too-long\b', line.last.value) 235 236 237def _LineHasContinuationMarkers(line): 238 """Return true if the line has continuation markers in it.""" 239 return any(tok.is_continuation for tok in line.tokens) 240 241 242def _CanPlaceOnSingleLine(line): 243 """Determine if the logical line can go on a single line. 244 245 Arguments: 246 line: (logical_line.LogicalLine) The line currently being formatted. 247 248 Returns: 249 True if the line can or should be added to a single line. False otherwise. 250 """ 251 token_names = [x.name for x in line.tokens] 252 if (style.Get('FORCE_MULTILINE_DICT') and 'LBRACE' in token_names): 253 return False 254 indent_amt = style.Get('INDENT_WIDTH') * line.depth 255 last = line.last 256 last_index = -1 257 if (last.is_pylint_comment or last.is_pytype_comment or 258 last.is_copybara_comment): 259 last = last.previous_token 260 last_index = -2 261 if last is None: 262 return True 263 return (last.total_length + indent_amt <= style.Get('COLUMN_LIMIT') and 264 not any(tok.is_comment for tok in line.tokens[:last_index])) 265 266 267def _AlignTrailingComments(final_lines): 268 """Align trailing comments to the same column.""" 269 final_lines_index = 0 270 while final_lines_index < len(final_lines): 271 line = final_lines[final_lines_index] 272 assert line.tokens 273 274 processed_content = False 275 276 for tok in line.tokens: 277 if (tok.is_comment and isinstance(tok.spaces_required_before, list) and 278 tok.value.startswith('#')): 279 # All trailing comments and comments that appear on a line by themselves 280 # in this block should be indented at the same level. The block is 281 # terminated by an empty line or EOF. Enumerate through each line in 282 # the block and calculate the max line length. Once complete, use the 283 # first col value greater than that value and create the necessary for 284 # each line accordingly. 285 all_pc_line_lengths = [] # All pre-comment line lengths 286 max_line_length = 0 287 288 while True: 289 # EOF 290 if final_lines_index + len(all_pc_line_lengths) == len(final_lines): 291 break 292 293 this_line = final_lines[final_lines_index + len(all_pc_line_lengths)] 294 295 # Blank line - note that content is preformatted so we don't need to 296 # worry about spaces/tabs; a blank line will always be '\n\n'. 297 assert this_line.tokens 298 if (all_pc_line_lengths and 299 this_line.tokens[0].formatted_whitespace_prefix.startswith('\n\n') 300 ): 301 break 302 303 if this_line.disable: 304 all_pc_line_lengths.append([]) 305 continue 306 307 # Calculate the length of each line in this logical line. 308 line_content = '' 309 pc_line_lengths = [] 310 311 for line_tok in this_line.tokens: 312 whitespace_prefix = line_tok.formatted_whitespace_prefix 313 314 newline_index = whitespace_prefix.rfind('\n') 315 if newline_index != -1: 316 max_line_length = max(max_line_length, len(line_content)) 317 line_content = '' 318 319 whitespace_prefix = whitespace_prefix[newline_index + 1:] 320 321 if line_tok.is_comment: 322 pc_line_lengths.append(len(line_content)) 323 else: 324 line_content += '{}{}'.format(whitespace_prefix, line_tok.value) 325 326 if pc_line_lengths: 327 max_line_length = max(max_line_length, max(pc_line_lengths)) 328 329 all_pc_line_lengths.append(pc_line_lengths) 330 331 # Calculate the aligned column value 332 max_line_length += 2 333 334 aligned_col = None 335 for potential_col in tok.spaces_required_before: 336 if potential_col > max_line_length: 337 aligned_col = potential_col 338 break 339 340 if aligned_col is None: 341 aligned_col = max_line_length 342 343 # Update the comment token values based on the aligned values 344 for all_pc_line_lengths_index, pc_line_lengths in enumerate( 345 all_pc_line_lengths): 346 if not pc_line_lengths: 347 continue 348 349 this_line = final_lines[final_lines_index + all_pc_line_lengths_index] 350 351 pc_line_length_index = 0 352 for line_tok in this_line.tokens: 353 if line_tok.is_comment: 354 assert pc_line_length_index < len(pc_line_lengths) 355 assert pc_line_lengths[pc_line_length_index] < aligned_col 356 357 # Note that there may be newlines embedded in the comments, so 358 # we need to apply a whitespace prefix to each line. 359 whitespace = ' ' * ( 360 aligned_col - pc_line_lengths[pc_line_length_index] - 1) 361 pc_line_length_index += 1 362 363 line_content = [] 364 365 for comment_line_index, comment_line in enumerate( 366 line_tok.value.split('\n')): 367 line_content.append('{}{}'.format(whitespace, 368 comment_line.strip())) 369 370 if comment_line_index == 0: 371 whitespace = ' ' * (aligned_col - 1) 372 373 line_content = '\n'.join(line_content) 374 375 # Account for initial whitespace already slated for the 376 # beginning of the line. 377 existing_whitespace_prefix = \ 378 line_tok.formatted_whitespace_prefix.lstrip('\n') 379 380 if line_content.startswith(existing_whitespace_prefix): 381 line_content = line_content[len(existing_whitespace_prefix):] 382 383 line_tok.value = line_content 384 385 assert pc_line_length_index == len(pc_line_lengths) 386 387 final_lines_index += len(all_pc_line_lengths) 388 389 processed_content = True 390 break 391 392 if not processed_content: 393 final_lines_index += 1 394 395 396def _FormatFinalLines(final_lines, verify): 397 """Compose the final output from the finalized lines.""" 398 formatted_code = [] 399 for line in final_lines: 400 formatted_line = [] 401 for tok in line.tokens: 402 if not tok.is_pseudo: 403 formatted_line.append(tok.formatted_whitespace_prefix) 404 formatted_line.append(tok.value) 405 elif (not tok.next_token.whitespace_prefix.startswith('\n') and 406 not tok.next_token.whitespace_prefix.startswith(' ')): 407 if (tok.previous_token.value == ':' or 408 tok.next_token.value not in ',}])'): 409 formatted_line.append(' ') 410 411 formatted_code.append(''.join(formatted_line)) 412 if verify: 413 verifier.VerifyCode(formatted_code[-1]) 414 415 return ''.join(formatted_code) + '\n' 416 417 418class _StateNode(object): 419 """An edge in the solution space from 'previous.state' to 'state'. 420 421 Attributes: 422 state: (format_decision_state.FormatDecisionState) The format decision state 423 for this node. 424 newline: If True, then on the edge from 'previous.state' to 'state' a 425 newline is inserted. 426 previous: (_StateNode) The previous state node in the graph. 427 """ 428 429 # TODO(morbo): Add a '__cmp__' method. 430 431 def __init__(self, state, newline, previous): 432 self.state = state.Clone() 433 self.newline = newline 434 self.previous = previous 435 436 def __repr__(self): # pragma: no cover 437 return 'StateNode(state=[\n{0}\n], newline={1})'.format( 438 self.state, self.newline) 439 440 441# A tuple of (penalty, count) that is used to prioritize the BFS. In case of 442# equal penalties, we prefer states that were inserted first. During state 443# generation, we make sure that we insert states first that break the line as 444# late as possible. 445_OrderedPenalty = collections.namedtuple('OrderedPenalty', ['penalty', 'count']) 446 447# An item in the prioritized BFS search queue. The 'StateNode's 'state' has 448# the given '_OrderedPenalty'. 449_QueueItem = collections.namedtuple('QueueItem', 450 ['ordered_penalty', 'state_node']) 451 452 453def _AnalyzeSolutionSpace(initial_state): 454 """Analyze the entire solution space starting from initial_state. 455 456 This implements a variant of Dijkstra's algorithm on the graph that spans 457 the solution space (LineStates are the nodes). The algorithm tries to find 458 the shortest path (the one with the lowest penalty) from 'initial_state' to 459 the state where all tokens are placed. 460 461 Arguments: 462 initial_state: (format_decision_state.FormatDecisionState) The initial state 463 to start the search from. 464 465 Returns: 466 True if a formatting solution was found. False otherwise. 467 """ 468 count = 0 469 seen = set() 470 p_queue = [] 471 472 # Insert start element. 473 node = _StateNode(initial_state, False, None) 474 heapq.heappush(p_queue, _QueueItem(_OrderedPenalty(0, count), node)) 475 476 count += 1 477 while p_queue: 478 item = p_queue[0] 479 penalty = item.ordered_penalty.penalty 480 node = item.state_node 481 if not node.state.next_token: 482 break 483 heapq.heappop(p_queue) 484 485 if count > 10000: 486 node.state.ignore_stack_for_comparison = True 487 488 # Unconditionally add the state and check if it was present to avoid having 489 # to hash it twice in the common case (state hashing is expensive). 490 before_seen_count = len(seen) 491 seen.add(node.state) 492 # If seen didn't change size, the state was already present. 493 if before_seen_count == len(seen): 494 continue 495 496 # FIXME(morbo): Add a 'decision' element? 497 498 count = _AddNextStateToQueue(penalty, node, False, count, p_queue) 499 count = _AddNextStateToQueue(penalty, node, True, count, p_queue) 500 501 if not p_queue: 502 # We weren't able to find a solution. Do nothing. 503 return False 504 505 _ReconstructPath(initial_state, heapq.heappop(p_queue).state_node) 506 return True 507 508 509def _AddNextStateToQueue(penalty, previous_node, newline, count, p_queue): 510 """Add the following state to the analysis queue. 511 512 Assume the current state is 'previous_node' and has been reached with a 513 penalty of 'penalty'. Insert a line break if 'newline' is True. 514 515 Arguments: 516 penalty: (int) The penalty associated with the path up to this point. 517 previous_node: (_StateNode) The last _StateNode inserted into the priority 518 queue. 519 newline: (bool) Add a newline if True. 520 count: (int) The number of elements in the queue. 521 p_queue: (heapq) The priority queue representing the solution space. 522 523 Returns: 524 The updated number of elements in the queue. 525 """ 526 must_split = previous_node.state.MustSplit() 527 if newline and not previous_node.state.CanSplit(must_split): 528 # Don't add a newline if the token cannot be split. 529 return count 530 if not newline and must_split: 531 # Don't add a token we must split but where we aren't splitting. 532 return count 533 534 node = _StateNode(previous_node.state, newline, previous_node) 535 penalty += node.state.AddTokenToState( 536 newline=newline, dry_run=True, must_split=must_split) 537 heapq.heappush(p_queue, _QueueItem(_OrderedPenalty(penalty, count), node)) 538 return count + 1 539 540 541def _ReconstructPath(initial_state, current): 542 """Reconstruct the path through the queue with lowest penalty. 543 544 Arguments: 545 initial_state: (format_decision_state.FormatDecisionState) The initial state 546 to start the search from. 547 current: (_StateNode) The node in the decision graph that is the end point 548 of the path with the least penalty. 549 """ 550 path = collections.deque() 551 552 while current.previous: 553 path.appendleft(current) 554 current = current.previous 555 556 for node in path: 557 initial_state.AddTokenToState(newline=node.newline, dry_run=False) 558 559 560NESTED_DEPTH = [] 561 562 563def _FormatFirstToken(first_token, indent_depth, prev_line, final_lines): 564 """Format the first token in the logical line. 565 566 Add a newline and the required indent before the first token of the logical 567 line. 568 569 Arguments: 570 first_token: (format_token.FormatToken) The first token in the logical line. 571 indent_depth: (int) The line's indentation depth. 572 prev_line: (list of logical_line.LogicalLine) The logical line previous to 573 this line. 574 final_lines: (list of logical_line.LogicalLine) The logical lines that have 575 already been processed. 576 """ 577 global NESTED_DEPTH 578 while NESTED_DEPTH and NESTED_DEPTH[-1] > indent_depth: 579 NESTED_DEPTH.pop() 580 581 first_nested = False 582 if _IsClassOrDef(first_token): 583 if not NESTED_DEPTH: 584 NESTED_DEPTH = [indent_depth] 585 elif NESTED_DEPTH[-1] < indent_depth: 586 first_nested = True 587 NESTED_DEPTH.append(indent_depth) 588 589 first_token.AddWhitespacePrefix( 590 _CalculateNumberOfNewlines(first_token, indent_depth, prev_line, 591 final_lines, first_nested), 592 indent_level=indent_depth) 593 594 595NO_BLANK_LINES = 1 596ONE_BLANK_LINE = 2 597TWO_BLANK_LINES = 3 598 599 600def _IsClassOrDef(tok): 601 if tok.value in {'class', 'def', '@'}: 602 return True 603 return (tok.next_token and tok.value == 'async' and 604 tok.next_token.value == 'def') 605 606 607def _CalculateNumberOfNewlines(first_token, indent_depth, prev_line, 608 final_lines, first_nested): 609 """Calculate the number of newlines we need to add. 610 611 Arguments: 612 first_token: (format_token.FormatToken) The first token in the logical 613 line. 614 indent_depth: (int) The line's indentation depth. 615 prev_line: (list of logical_line.LogicalLine) The logical line previous to 616 this line. 617 final_lines: (list of logical_line.LogicalLine) The logical lines that have 618 already been processed. 619 first_nested: (boolean) Whether this is the first nested class or function. 620 621 Returns: 622 The number of newlines needed before the first token. 623 """ 624 # TODO(morbo): Special handling for imports. 625 # TODO(morbo): Create a knob that can tune these. 626 if prev_line is None: 627 # The first line in the file. Don't add blank lines. 628 # FIXME(morbo): Is this correct? 629 if first_token.newlines is not None: 630 first_token.newlines = None 631 return 0 632 633 if first_token.is_docstring: 634 if (prev_line.first.value == 'class' and 635 style.Get('BLANK_LINE_BEFORE_CLASS_DOCSTRING')): 636 # Enforce a blank line before a class's docstring. 637 return ONE_BLANK_LINE 638 elif (prev_line.first.value.startswith('#') and 639 style.Get('BLANK_LINE_BEFORE_MODULE_DOCSTRING')): 640 # Enforce a blank line before a module's docstring. 641 return ONE_BLANK_LINE 642 # The docstring shouldn't have a newline before it. 643 return NO_BLANK_LINES 644 645 if first_token.is_name and not indent_depth: 646 if prev_line.first.value in {'from', 'import'}: 647 # Support custom number of blank lines between top-level imports and 648 # variable definitions. 649 return 1 + style.Get( 650 'BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES') 651 652 prev_last_token = prev_line.last 653 if prev_last_token.is_docstring: 654 if (not indent_depth and first_token.value in {'class', 'def', 'async'}): 655 # Separate a class or function from the module-level docstring with 656 # appropriate number of blank lines. 657 return 1 + style.Get('BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION') 658 if (first_nested and 659 not style.Get('BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF') and 660 _IsClassOrDef(first_token)): 661 first_token.newlines = None 662 return NO_BLANK_LINES 663 if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token, 664 prev_last_token): 665 return NO_BLANK_LINES 666 else: 667 return ONE_BLANK_LINE 668 669 if _IsClassOrDef(first_token): 670 # TODO(morbo): This can go once the blank line calculator is more 671 # sophisticated. 672 if not indent_depth: 673 # This is a top-level class or function. 674 is_inline_comment = prev_last_token.whitespace_prefix.count('\n') == 0 675 if (not prev_line.disable and prev_last_token.is_comment and 676 not is_inline_comment): 677 # This token follows a non-inline comment. 678 if _NoBlankLinesBeforeCurrentToken(prev_last_token.value, first_token, 679 prev_last_token): 680 # Assume that the comment is "attached" to the current line. 681 # Therefore, we want two blank lines before the comment. 682 index = len(final_lines) - 1 683 while index > 0: 684 if not final_lines[index - 1].is_comment: 685 break 686 index -= 1 687 if final_lines[index - 1].first.value == '@': 688 final_lines[index].first.AdjustNewlinesBefore(NO_BLANK_LINES) 689 else: 690 prev_last_token.AdjustNewlinesBefore( 691 1 + style.Get('BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION')) 692 if first_token.newlines is not None: 693 first_token.newlines = None 694 return NO_BLANK_LINES 695 elif _IsClassOrDef(prev_line.first): 696 if first_nested and not style.Get( 697 'BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'): 698 first_token.newlines = None 699 return NO_BLANK_LINES 700 701 # Calculate how many newlines were between the original lines. We want to 702 # retain that formatting if it doesn't violate one of the style guide rules. 703 if first_token.is_comment: 704 first_token_lineno = first_token.lineno - first_token.value.count('\n') 705 else: 706 first_token_lineno = first_token.lineno 707 708 prev_last_token_lineno = prev_last_token.lineno 709 if prev_last_token.is_multiline_string: 710 prev_last_token_lineno += prev_last_token.value.count('\n') 711 712 if first_token_lineno - prev_last_token_lineno > 1: 713 return ONE_BLANK_LINE 714 715 return NO_BLANK_LINES 716 717 718def _SingleOrMergedLines(lines): 719 """Generate the lines we want to format. 720 721 Arguments: 722 lines: (list of logical_line.LogicalLine) Lines we want to format. 723 724 Yields: 725 Either a single line, if the current line cannot be merged with the 726 succeeding line, or the next two lines merged into one line. 727 """ 728 index = 0 729 last_was_merged = False 730 while index < len(lines): 731 if lines[index].disable: 732 line = lines[index] 733 index += 1 734 while index < len(lines): 735 column = line.last.column + 2 736 if lines[index].lineno != line.lineno: 737 break 738 if line.last.value != ':': 739 leaf = pytree.Leaf( 740 type=token.SEMI, value=';', context=('', (line.lineno, column))) 741 line.AppendToken(format_token.FormatToken(leaf)) 742 for tok in lines[index].tokens: 743 line.AppendToken(tok) 744 index += 1 745 yield line 746 elif line_joiner.CanMergeMultipleLines(lines[index:], last_was_merged): 747 # TODO(morbo): This splice is potentially very slow. Come up with a more 748 # performance-friendly way of determining if two lines can be merged. 749 next_line = lines[index + 1] 750 for tok in next_line.tokens: 751 lines[index].AppendToken(tok) 752 if (len(next_line.tokens) == 1 and next_line.first.is_multiline_string): 753 # This may be a multiline shebang. In that case, we want to retain the 754 # formatting. Otherwise, it could mess up the shell script's syntax. 755 lines[index].disable = True 756 yield lines[index] 757 index += 2 758 last_was_merged = True 759 else: 760 yield lines[index] 761 index += 1 762 last_was_merged = False 763 764 765def _NoBlankLinesBeforeCurrentToken(text, cur_token, prev_token): 766 """Determine if there are no blank lines before the current token. 767 768 The previous token is a docstring or comment. The prev_token_lineno is the 769 start of the text of that token. Counting the number of newlines in its text 770 gives us the extent and thus where the line number of the end of the 771 docstring or comment. After that, we just compare it to the current token's 772 line number to see if there are blank lines between them. 773 774 Arguments: 775 text: (unicode) The text of the docstring or comment before the current 776 token. 777 cur_token: (format_token.FormatToken) The current token in the logical line. 778 prev_token: (format_token.FormatToken) The previous token in the logical 779 line. 780 781 Returns: 782 True if there is no blank line before the current token. 783 """ 784 cur_token_lineno = cur_token.lineno 785 if cur_token.is_comment: 786 cur_token_lineno -= cur_token.value.count('\n') 787 num_newlines = text.count('\n') if not prev_token.is_comment else 0 788 return prev_token.lineno + num_newlines == cur_token_lineno - 1 789