line_joiner.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. """Join logical lines together.
  15. Determine how many lines can be joined into one line. For instance, we could
  16. join these statements into one line:
  17. if a == 42:
  18. continue
  19. like this:
  20. if a == 42: continue
  21. There are a few restrictions:
  22. 1. The lines should have been joined in the original source.
  23. 2. The joined lines must not go over the column boundary if placed on the same
  24. line.
  25. 3. They need to be very simple statements.
  26. Note: Because we don't allow the use of a semicolon to separate statements, it
  27. follows that there can only be at most two lines to join.
  28. """
  29. from yapf.yapflib import style
  30. _CLASS_OR_FUNC = frozenset({'def', 'class'})
  31. def CanMergeMultipleLines(lines, last_was_merged=False):
  32. """Determine if multiple lines can be joined into one.
  33. Arguments:
  34. lines: (list of LogicalLine) This is a splice of LogicalLines from the full
  35. code base.
  36. last_was_merged: (bool) The last line was merged.
  37. Returns:
  38. True if two consecutive lines can be joined together. In reality, this will
  39. only happen if two consecutive lines can be joined, due to the style guide.
  40. """
  41. # The indentation amount for the starting line (number of spaces).
  42. indent_amt = lines[0].depth * style.Get('INDENT_WIDTH')
  43. if len(lines) == 1 or indent_amt > style.Get('COLUMN_LIMIT'):
  44. return False
  45. if (len(lines) >= 3 and lines[2].depth >= lines[1].depth and
  46. lines[0].depth != lines[2].depth):
  47. # If lines[2]'s depth is greater than or equal to line[1]'s depth, we're not
  48. # looking at a single statement (e.g., if-then, while, etc.). A following
  49. # line with the same depth as the first line isn't part of the lines we
  50. # would want to combine.
  51. return False # Don't merge more than two lines together.
  52. if lines[0].first.value in _CLASS_OR_FUNC:
  53. # Don't join lines onto the starting line of a class or function.
  54. return False
  55. limit = style.Get('COLUMN_LIMIT') - indent_amt
  56. if lines[0].last.total_length < limit:
  57. limit -= lines[0].last.total_length
  58. if lines[0].first.value == 'if':
  59. return _CanMergeLineIntoIfStatement(lines, limit)
  60. if last_was_merged and lines[0].first.value in {'elif', 'else'}:
  61. return _CanMergeLineIntoIfStatement(lines, limit)
  62. # TODO(morbo): Other control statements?
  63. return False
  64. def _CanMergeLineIntoIfStatement(lines, limit):
  65. """Determine if we can merge a short if-then statement into one line.
  66. Two lines of an if-then statement can be merged if they were that way in the
  67. original source, fit on the line without going over the column limit, and are
  68. considered "simple" statements --- typically statements like 'pass',
  69. 'continue', and 'break'.
  70. Arguments:
  71. lines: (list of LogicalLine) The lines we are wanting to merge.
  72. limit: (int) The amount of space remaining on the line.
  73. Returns:
  74. True if the lines can be merged, False otherwise.
  75. """
  76. if len(lines[1].tokens) == 1 and lines[1].last.is_multiline_string:
  77. # This might be part of a multiline shebang.
  78. return True
  79. if lines[0].lineno != lines[1].lineno:
  80. # Don't merge lines if the original lines weren't merged.
  81. return False
  82. if lines[1].last.total_length >= limit:
  83. # Don't merge lines if the result goes over the column limit.
  84. return False
  85. return style.Get('JOIN_MULTIPLE_LINES')