fixdictsformatting.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. from __future__ import unicode_literals
  2. import sys
  3. from lib2to3.pytree import Node, Leaf
  4. from lib2to3.fixer_util import token, syms
  5. from yapf.yapflib import pytree_utils
  6. from django.utils import six
  7. from .config import yapf as yapf_config
  8. MAX_LINE_LENGTH = yapf_config.getint('style', 'column_limit') + 1
  9. def fix_formatting(filesource):
  10. if not ('{' in filesource and ('[' in filesource or '(' in filesource)):
  11. return filesource
  12. tree = pytree_utils.ParseCodeToTree(filesource)
  13. for node in tree.children:
  14. walk_tree(node, node.children)
  15. return six.text_type(tree)
  16. def walk_tree(node, children):
  17. for item in children:
  18. if item.type == syms.dictsetmaker:
  19. indent = item.parent.children[-1].column
  20. walk_dict_tree(item, item.children, indent)
  21. else:
  22. walk_tree(item, item.children)
  23. def walk_dict_tree(node, children, indent):
  24. for item in children:
  25. prev = item.prev_sibling
  26. if isinstance(prev, Leaf) and prev.value == ':':
  27. if isinstance(item, Leaf):
  28. if six.text_type(item).startswith("\n"):
  29. item.replace(Leaf(
  30. item.type,
  31. item.value,
  32. prefix=' ',
  33. ))
  34. elif six.text_type(item).strip()[0] in ('[', '{'):
  35. walk_tree(item, item.children)
  36. else:
  37. walk_dedent_tree(item, item.children, indent)
  38. def walk_dedent_tree(node, children, indent):
  39. force_split_next = False
  40. for item in children:
  41. prev = item.prev_sibling
  42. if not prev:
  43. if isinstance(item, Leaf) and six.text_type(item).startswith("\n"):
  44. prev = node.prev_sibling
  45. next = node.next_sibling
  46. final_length = 0
  47. if prev and "\n" not in six.text_type(node).strip():
  48. final_length = prev.column + len(six.text_type(node).strip()) + 3
  49. item.replace(Leaf(
  50. item.type,
  51. item.value,
  52. prefix=' ',
  53. ))
  54. if final_length and final_length > MAX_LINE_LENGTH:
  55. # tell next call to walk_dedent_tree_node that we need
  56. # different stringformat tactic
  57. force_split_next = True
  58. elif isinstance(item, Node):
  59. if node.type == syms.power:
  60. for subitem in item.children[1:]:
  61. walk_dedent_power_node(subitem, subitem.children, indent)
  62. else:
  63. for subitem in item.children[1:]:
  64. walk_dedent_tree_node(subitem, subitem.children, indent, force_split_next)
  65. force_split_next = False
  66. def walk_dedent_tree_node(node, children, indent, force_split_next=False):
  67. if six.text_type(node).startswith("\n"):
  68. if isinstance(node, Leaf):
  69. prev = node.prev_sibling
  70. next = node.next_sibling
  71. is_followup = prev and prev.type == token.STRING and node.type == token.STRING
  72. if is_followup:
  73. new_value = node.value
  74. # insert linebreak after last string in braces, so its closing brace moves to new line
  75. if not node.next_sibling:
  76. closing_bracket = node.parent.parent.children[-1]
  77. if not six.text_type(closing_bracket).startswith("\n"):
  78. new_value = "%s\n%s" % (node.value, (' ' * (indent + 4)))
  79. node.replace(Leaf(
  80. node.type,
  81. new_value,
  82. prefix="\n%s" % (' ' * (indent + 8)),
  83. ))
  84. else:
  85. if six.text_type(node).strip() in (')', '}'):
  86. new_prefix = "\n%s" % (' ' * (indent + 4))
  87. else:
  88. new_prefix = "\n%s" % (' ' * (indent + 8))
  89. node.replace(Leaf(
  90. node.type,
  91. node.value,
  92. prefix=new_prefix
  93. ))
  94. else:
  95. for item in children:
  96. walk_dedent_tree_node(item, item.children, indent)
  97. elif isinstance(node, Leaf):
  98. if node.type == token.STRING:
  99. strings_tuple = node.parent.parent
  100. prev = node.prev_sibling
  101. next = node.next_sibling
  102. is_opening = prev is None and six.text_type(strings_tuple).strip()[0] == '('
  103. has_followup = next and next.type == token.STRING
  104. if is_opening and has_followup:
  105. node.replace(Leaf(
  106. node.type,
  107. node.value,
  108. prefix="\n%s" % (' ' * (indent + 8)),
  109. ))
  110. elif force_split_next:
  111. node.replace(Leaf(
  112. node.type,
  113. "%s\n%s" % (node.value, (' ' * (indent + 4))),
  114. prefix="\n%s" % (' ' * (indent + 8)),
  115. ))
  116. else:
  117. for item in children:
  118. walk_dedent_tree_node(item, item.children, indent)
  119. def walk_dedent_power_node(node, children, indent):
  120. if isinstance(node, Leaf):
  121. if six.text_type(node).startswith("\n"):
  122. node.replace(Leaf(
  123. node.type,
  124. node.value,
  125. prefix=node.prefix[:-4],
  126. ))
  127. else:
  128. for item in children:
  129. walk_dedent_power_node(item, item.children, indent)