xref: /aosp_15_r20/external/yapf/yapf/yapflib/reformatter.py (revision 7249d1a64f4850ccf838e62a46276f891f72998e)
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