fixdictsformatting.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. walk_dict_tree(item, item.children)
  20. else:
  21. walk_tree(item, item.children)
  22. def walk_dict_tree(node, children):
  23. for item in children:
  24. prev = item.prev_sibling
  25. if isinstance(prev, Leaf) and prev.value == ':':
  26. if isinstance(item, Leaf):
  27. if six.text_type(item).startswith("\n"):
  28. item.replace(Leaf(
  29. item.type,
  30. item.value,
  31. prefix=' ',
  32. ))
  33. elif six.text_type(item).strip()[0] in ('[', '{'):
  34. walk_tree(item, item.children)
  35. else:
  36. walk_dedent_tree(item, item.children)
  37. def walk_dedent_tree(node, children):
  38. force_split_next = False
  39. for item in children:
  40. prev = item.prev_sibling
  41. if not prev:
  42. if isinstance(item, Leaf) and six.text_type(item).startswith("\n"):
  43. prev = node.prev_sibling
  44. next = node.next_sibling
  45. final_length = 0
  46. if prev and "\n" not in six.text_type(node).strip():
  47. final_length = prev.column + len(six.text_type(node).strip()) + 3
  48. item.replace(Leaf(
  49. item.type,
  50. item.value,
  51. prefix=' ',
  52. ))
  53. if final_length and final_length > MAX_LINE_LENGTH:
  54. # tell next call to walk_dedent_tree_node that we need
  55. # different stringformat tactic
  56. force_split_next = True
  57. elif isinstance(item, Node):
  58. for subitem in item.children[1:]:
  59. walk_dedent_tree_node(subitem, subitem.children, force_split_next)
  60. force_split_next = False
  61. def walk_dedent_tree_node(node, children, force_split_next=False):
  62. if six.text_type(node).startswith("\n"):
  63. if isinstance(node, Leaf):
  64. prev = node.prev_sibling
  65. is_followup = prev and prev.type == token.STRING and node.type == token.STRING
  66. if is_followup:
  67. new_value = node.value
  68. new_prefix = "\n%s" % (' ' * (len(prev.prefix.lstrip("\n")) / 4 * 4))
  69. # insert linebreak after last string in braces, so its closing brace moves to new line
  70. if not node.next_sibling:
  71. closing_bracket = node.parent.parent.children[-1]
  72. if not six.text_type(closing_bracket).startswith("\n"):
  73. new_value = "%s\n%s" % (node.value, (' ' * ((len(prev.prefix.lstrip("\n")) / 4 - 1) * 4)))
  74. node.replace(Leaf(
  75. node.type,
  76. new_value,
  77. prefix=new_prefix,
  78. ))
  79. else:
  80. node.replace(Leaf(
  81. node.type,
  82. node.value,
  83. prefix=node.prefix[:-4],
  84. ))
  85. else:
  86. for item in children:
  87. walk_dedent_tree_node(item, item.children)
  88. elif isinstance(node, Leaf):
  89. if node.type == token.STRING:
  90. strings_tuple = node.parent.parent
  91. # compute indent
  92. if force_split_next:
  93. container = strings_tuple.parent.children[0]
  94. else:
  95. container = strings_tuple.parent.parent.children[0]
  96. while isinstance(container, Node):
  97. container = container.children[0]
  98. indent = container.column + 4
  99. prev = node.prev_sibling
  100. next = node.next_sibling
  101. is_opening = prev is None and six.text_type(strings_tuple).strip()[0] == '('
  102. has_followup = next and next.type == token.STRING
  103. if is_opening and has_followup:
  104. node.replace(Leaf(
  105. node.type,
  106. node.value,
  107. prefix="\n%s" % (' ' * indent),
  108. ))
  109. elif force_split_next:
  110. node.replace(Leaf(
  111. node.type,
  112. "%s\n%s" % (node.value, (' ' * (indent - 4))),
  113. prefix="\n%s" % (' ' * indent),
  114. ))
  115. else:
  116. for item in children:
  117. walk_dedent_tree_node(item, item.children)