xref: /minix3/external/bsd/llvm/dist/clang/tools/clang-format/clang-format-diff.py (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc#!/usr/bin/env python
2f4a2713aSLionel Sambuc#
3f4a2713aSLionel Sambuc#===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
4f4a2713aSLionel Sambuc#
5f4a2713aSLionel Sambuc#                     The LLVM Compiler Infrastructure
6f4a2713aSLionel Sambuc#
7f4a2713aSLionel Sambuc# This file is distributed under the University of Illinois Open Source
8f4a2713aSLionel Sambuc# License. See LICENSE.TXT for details.
9f4a2713aSLionel Sambuc#
10f4a2713aSLionel Sambuc#===------------------------------------------------------------------------===#
11f4a2713aSLionel Sambuc
12f4a2713aSLionel Sambucr"""
13f4a2713aSLionel SambucClangFormat Diff Reformatter
14f4a2713aSLionel Sambuc============================
15f4a2713aSLionel Sambuc
16f4a2713aSLionel SambucThis script reads input from a unified diff and reformats all the changed
17f4a2713aSLionel Sambuclines. This is useful to reformat all the lines touched by a specific patch.
18*0a6a1f1dSLionel SambucExample usage for git/svn users:
19f4a2713aSLionel Sambuc
20f4a2713aSLionel Sambuc  git diff -U0 HEAD^ | clang-format-diff.py -p1 -i
21*0a6a1f1dSLionel Sambuc  svn diff --diff-cmd=diff -x-U0 | clang-format-diff.py -i
22f4a2713aSLionel Sambuc
23f4a2713aSLionel Sambuc"""
24f4a2713aSLionel Sambuc
25f4a2713aSLionel Sambucimport argparse
26f4a2713aSLionel Sambucimport difflib
27f4a2713aSLionel Sambucimport re
28f4a2713aSLionel Sambucimport string
29f4a2713aSLionel Sambucimport subprocess
30f4a2713aSLionel Sambucimport StringIO
31f4a2713aSLionel Sambucimport sys
32f4a2713aSLionel Sambuc
33f4a2713aSLionel Sambuc
34f4a2713aSLionel Sambuc# Change this to the full path if clang-format is not on the path.
35f4a2713aSLionel Sambucbinary = 'clang-format'
36f4a2713aSLionel Sambuc
37f4a2713aSLionel Sambuc
38f4a2713aSLionel Sambucdef main():
39f4a2713aSLionel Sambuc  parser = argparse.ArgumentParser(description=
40f4a2713aSLionel Sambuc                                   'Reformat changed lines in diff. Without -i '
41f4a2713aSLionel Sambuc                                   'option just output the diff that would be '
42f4a2713aSLionel Sambuc                                   'introduced.')
43f4a2713aSLionel Sambuc  parser.add_argument('-i', action='store_true', default=False,
44f4a2713aSLionel Sambuc                      help='apply edits to files instead of displaying a diff')
45*0a6a1f1dSLionel Sambuc  parser.add_argument('-p', metavar='NUM', default=0,
46f4a2713aSLionel Sambuc                      help='strip the smallest prefix containing P slashes')
47*0a6a1f1dSLionel Sambuc  parser.add_argument('-regex', metavar='PATTERN', default=None,
48*0a6a1f1dSLionel Sambuc                      help='custom pattern selecting file paths to reformat '
49*0a6a1f1dSLionel Sambuc                      '(case sensitive, overrides -iregex)')
50*0a6a1f1dSLionel Sambuc  parser.add_argument('-iregex', metavar='PATTERN', default=
51*0a6a1f1dSLionel Sambuc                      r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|proto'
52*0a6a1f1dSLionel Sambuc                      r'|protodevel|java)',
53*0a6a1f1dSLionel Sambuc                      help='custom pattern selecting file paths to reformat '
54*0a6a1f1dSLionel Sambuc                      '(case insensitive, overridden by -regex)')
55*0a6a1f1dSLionel Sambuc  parser.add_argument('-v', '--verbose', action='store_true',
56*0a6a1f1dSLionel Sambuc                      help='be more verbose, ineffective without -i')
57f4a2713aSLionel Sambuc  parser.add_argument(
58f4a2713aSLionel Sambuc      '-style',
59f4a2713aSLionel Sambuc      help=
60f4a2713aSLionel Sambuc      'formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)')
61f4a2713aSLionel Sambuc  args = parser.parse_args()
62f4a2713aSLionel Sambuc
63f4a2713aSLionel Sambuc  # Extract changed lines for each file.
64f4a2713aSLionel Sambuc  filename = None
65f4a2713aSLionel Sambuc  lines_by_file = {}
66f4a2713aSLionel Sambuc  for line in sys.stdin:
67f4a2713aSLionel Sambuc    match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
68f4a2713aSLionel Sambuc    if match:
69f4a2713aSLionel Sambuc      filename = match.group(2)
70f4a2713aSLionel Sambuc    if filename == None:
71f4a2713aSLionel Sambuc      continue
72f4a2713aSLionel Sambuc
73*0a6a1f1dSLionel Sambuc    if args.regex is not None:
74*0a6a1f1dSLionel Sambuc      if not re.match('^%s$' % args.regex, filename):
75*0a6a1f1dSLionel Sambuc        continue
76*0a6a1f1dSLionel Sambuc    else:
77*0a6a1f1dSLionel Sambuc      if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
78f4a2713aSLionel Sambuc        continue
79f4a2713aSLionel Sambuc
80f4a2713aSLionel Sambuc    match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
81f4a2713aSLionel Sambuc    if match:
82f4a2713aSLionel Sambuc      start_line = int(match.group(1))
83f4a2713aSLionel Sambuc      line_count = 1
84f4a2713aSLionel Sambuc      if match.group(3):
85f4a2713aSLionel Sambuc        line_count = int(match.group(3))
86f4a2713aSLionel Sambuc      if line_count == 0:
87f4a2713aSLionel Sambuc        continue
88f4a2713aSLionel Sambuc      end_line = start_line + line_count - 1;
89f4a2713aSLionel Sambuc      lines_by_file.setdefault(filename, []).extend(
90f4a2713aSLionel Sambuc          ['-lines', str(start_line) + ':' + str(end_line)])
91f4a2713aSLionel Sambuc
92f4a2713aSLionel Sambuc  # Reformat files containing changes in place.
93f4a2713aSLionel Sambuc  for filename, lines in lines_by_file.iteritems():
94*0a6a1f1dSLionel Sambuc    if args.i and args.verbose:
95*0a6a1f1dSLionel Sambuc      print 'Formatting', filename
96f4a2713aSLionel Sambuc    command = [binary, filename]
97f4a2713aSLionel Sambuc    if args.i:
98f4a2713aSLionel Sambuc      command.append('-i')
99f4a2713aSLionel Sambuc    command.extend(lines)
100f4a2713aSLionel Sambuc    if args.style:
101f4a2713aSLionel Sambuc      command.extend(['-style', args.style])
102f4a2713aSLionel Sambuc    p = subprocess.Popen(command, stdout=subprocess.PIPE,
103*0a6a1f1dSLionel Sambuc                         stderr=None, stdin=subprocess.PIPE)
104f4a2713aSLionel Sambuc    stdout, stderr = p.communicate()
105f4a2713aSLionel Sambuc    if p.returncode != 0:
106f4a2713aSLionel Sambuc      sys.exit(p.returncode);
107f4a2713aSLionel Sambuc
108f4a2713aSLionel Sambuc    if not args.i:
109f4a2713aSLionel Sambuc      with open(filename) as f:
110f4a2713aSLionel Sambuc        code = f.readlines()
111f4a2713aSLionel Sambuc      formatted_code = StringIO.StringIO(stdout).readlines()
112f4a2713aSLionel Sambuc      diff = difflib.unified_diff(code, formatted_code,
113f4a2713aSLionel Sambuc                                  filename, filename,
114f4a2713aSLionel Sambuc                                  '(before formatting)', '(after formatting)')
115f4a2713aSLionel Sambuc      diff_string = string.join(diff, '')
116f4a2713aSLionel Sambuc      if len(diff_string) > 0:
117*0a6a1f1dSLionel Sambuc        sys.stdout.write(diff_string)
118f4a2713aSLionel Sambuc
119f4a2713aSLionel Sambucif __name__ == '__main__':
120f4a2713aSLionel Sambuc  main()
121