clang-format-diff.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #!/usr/bin/python
  2. #
  3. #===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
  4. #
  5. # The LLVM Compiler Infrastructure
  6. #
  7. # This file is distributed under the University of Illinois Open Source
  8. # License. See LICENSE.TXT for details.
  9. #
  10. #===------------------------------------------------------------------------===#
  11. r"""
  12. ClangFormat Diff Reformatter
  13. ============================
  14. This script reads input from a unified diff and reformats all the changed
  15. lines. This is useful to reformat all the lines touched by a specific patch.
  16. Example usage for git users:
  17. git diff -U0 HEAD^ | clang-format-diff.py -p1
  18. """
  19. import argparse
  20. import re
  21. import subprocess
  22. import sys
  23. # Change this to the full path if clang-format is not on the path.
  24. binary = 'clang-format'
  25. def getOffsetLength(filename, line_number, line_count):
  26. """
  27. Calculates the field offset and length based on line number and count.
  28. """
  29. offset = 0
  30. length = 0
  31. with open(filename, 'r') as f:
  32. for line in f:
  33. if line_number > 1:
  34. offset += len(line)
  35. line_number -= 1
  36. elif line_count > 0:
  37. length += len(line)
  38. line_count -= 1
  39. else:
  40. break
  41. return offset, length
  42. def formatRange(r, style):
  43. """
  44. Formats range 'r' according to style 'style'.
  45. """
  46. filename, line_number, line_count = r
  47. # FIXME: Add other types containing C++/ObjC code.
  48. if not (filename.endswith(".cpp") or filename.endswith(".cc") or
  49. filename.endswith(".h")):
  50. return
  51. offset, length = getOffsetLength(filename, line_number, line_count)
  52. with open(filename, 'r') as f:
  53. text = f.read()
  54. command = [binary, '-offset', str(offset), '-length', str(length)]
  55. if style:
  56. command.extend(['-style', style])
  57. p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
  58. stdin=subprocess.PIPE)
  59. stdout, stderr = p.communicate(input=text)
  60. if stderr:
  61. print stderr
  62. return
  63. if not stdout:
  64. print 'Segfault occurred while formatting', filename
  65. print 'Please report a bug on llvm.org/bugs.'
  66. return
  67. with open(filename, 'w') as f:
  68. f.write(stdout)
  69. def main():
  70. parser = argparse.ArgumentParser(description=
  71. 'Reformat changed lines in diff.')
  72. parser.add_argument('-p', default=0,
  73. help='strip the smallest prefix containing P slashes')
  74. parser.add_argument(
  75. '-style',
  76. help=
  77. 'formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)')
  78. args = parser.parse_args()
  79. filename = None
  80. ranges = []
  81. for line in sys.stdin:
  82. match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
  83. if match:
  84. filename = match.group(2)
  85. if filename == None:
  86. continue
  87. match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
  88. if match:
  89. line_count = 1
  90. if match.group(3):
  91. line_count = int(match.group(3))
  92. ranges.append((filename, int(match.group(1)), line_count))
  93. # Reverse the ranges so that the reformatting does not influence file offsets.
  94. for r in reversed(ranges):
  95. # Do the actual formatting.
  96. formatRange(r, args.style)
  97. if __name__ == '__main__':
  98. main()