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