xref: /llvm-project/clang/tools/clang-format/clang-format-diff.py (revision 164c8e181068880f825baebf76e384e51f20c9c7)
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
12r"""
13ClangFormat Diff Reformatter
14============================
15
16This script reads input from a unified diff and reformats all the changed
17lines. This is useful to reformat all the lines touched by a specific patch.
18Example usage for git users:
19
20  git diff -U0 HEAD^ | clang-format-diff.py -p1
21
22"""
23
24import argparse
25import re
26import subprocess
27import sys
28
29
30# Change this to the full path if clang-format is not on the path.
31binary = 'clang-format'
32
33
34def main():
35  parser = argparse.ArgumentParser(description=
36                                   'Reformat changed lines in diff.')
37  parser.add_argument('-p', default=0,
38                      help='strip the smallest prefix containing P slashes')
39  parser.add_argument(
40      '-style',
41      help=
42      'formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)')
43  args = parser.parse_args()
44
45  # Extract changed lines for each file.
46  filename = None
47  lines_by_file = {}
48  for line in sys.stdin:
49    match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
50    if match:
51      filename = match.group(2)
52    if filename == None:
53      continue
54
55    # FIXME: Add other types containing C++/ObjC code.
56    if not (filename.endswith(".cpp") or filename.endswith(".cc") or
57            filename.endswith(".h")):
58      continue
59
60    match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
61    if match:
62      start_line = int(match.group(1))
63      line_count = 1
64      if match.group(3):
65        line_count = int(match.group(3))
66      if line_count == 0:
67        continue
68      end_line = start_line + line_count - 1;
69      lines_by_file.setdefault(filename, []).extend(
70          ['-lines', str(start_line) + ':' + str(end_line)])
71
72  # Reformat files containing changes in place.
73  for filename, lines in lines_by_file.iteritems():
74    command = [binary, '-i', filename]
75    command.extend(lines)
76    if args.style:
77      command.extend(['-style', args.style])
78    p = subprocess.Popen(command, stdout=subprocess.PIPE,
79                         stderr=subprocess.PIPE,
80                         stdin=subprocess.PIPE)
81    stdout, stderr = p.communicate()
82    if stderr:
83      print stderr
84      return
85
86
87if __name__ == '__main__':
88  main()
89