• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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
53def 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
83def main():
84  parser = argparse.ArgumentParser(description=
85                                   'Reformat changed lines in diff')
86  parser.add_argument('-p', default=0,
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
115if __name__ == '__main__':
116  main()
117