3 #===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
5 # The LLVM Compiler Infrastructure
7 # This file is distributed under the University of Illinois Open Source
10 # ============================================================
12 # University of Illinois/NCSA
15 # Copyright (c) 2007-2015 University of Illinois at Urbana-Champaign.
16 # All rights reserved.
22 # University of Illinois at Urbana-Champaign
26 # Permission is hereby granted, free of charge, to any person obtaining a copy of
27 # this software and associated documentation files (the "Software"), to deal with
28 # the Software without restriction, including without limitation the rights to
29 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
30 # of the Software, and to permit persons to whom the Software is furnished to do
31 # so, subject to the following conditions:
33 # * Redistributions of source code must retain the above copyright notice,
34 # this list of conditions and the following disclaimers.
36 # * Redistributions in binary form must reproduce the above copyright notice,
37 # this list of conditions and the following disclaimers in the
38 # documentation and/or other materials provided with the distribution.
40 # * Neither the names of the LLVM Team, University of Illinois at
41 # Urbana-Champaign, nor the names of its contributors may be used to
42 # endorse or promote products derived from this Software without specific
43 # prior written permission.
45 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
47 # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48 # CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
50 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
53 # ============================================================
55 #===------------------------------------------------------------------------===#
58 ClangFormat Diff Reformatter
59 ============================
61 This script reads input from a unified diff and reformats all the changed
62 lines. This is useful to reformat all the lines touched by a specific patch.
63 Example usage for git/svn users:
65 git diff -U0 HEAD^ | clang-format-diff.py -p1 -i
66 svn diff --diff-cmd=diff -x-U0 | clang-format-diff.py -i
79 # Change this to the full path if clang-format is not on the path.
80 binary
= 'clang-format'
84 parser
= argparse
.ArgumentParser(description
=
85 'Reformat changed lines in diff. Without -i '
86 'option just output the diff that would be '
88 parser
.add_argument('-i', action
='store_true', default
=False,
89 help='apply edits to files instead of displaying a diff')
90 parser
.add_argument('-p', metavar
='NUM', default
=0,
91 help='strip the smallest prefix containing P slashes')
92 parser
.add_argument('-regex', metavar
='PATTERN', default
=None,
93 help='custom pattern selecting file paths to reformat '
94 '(case sensitive, overrides -iregex)')
95 parser
.add_argument('-iregex', metavar
='PATTERN', default
=
96 r
'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|ts|proto'
98 help='custom pattern selecting file paths to reformat '
99 '(case insensitive, overridden by -regex)')
100 parser
.add_argument('-sort-includes', action
='store_true', default
=False,
101 help='let clang-format sort include blocks')
102 parser
.add_argument('-v', '--verbose', action
='store_true',
103 help='be more verbose, ineffective without -i')
104 args
= parser
.parse_args()
106 # Extract changed lines for each file.
109 for line
in sys
.stdin
:
110 match
= re
.search('^\+\+\+\ (.*?/){%s}(\S*)' % args
.p
, line
)
112 filename
= match
.group(2)
116 if args
.regex
is not None:
117 if not re
.match('^%s$' % args
.regex
, filename
):
120 if not re
.match('^%s$' % args
.iregex
, filename
, re
.IGNORECASE
):
123 match
= re
.search('^@@.*\+(\d+)(,(\d+))?', line
)
125 start_line
= int(match
.group(1))
128 line_count
= int(match
.group(3))
131 end_line
= start_line
+ line_count
- 1
132 lines_by_file
.setdefault(filename
, []).extend(
133 ['-lines', str(start_line
) + ':' + str(end_line
)])
135 # Reformat files containing changes in place.
136 for filename
, lines
in lines_by_file
.iteritems():
137 if args
.i
and args
.verbose
:
138 print 'Formatting', filename
139 command
= [binary
, filename
]
142 if args
.sort_includes
:
143 command
.append('-sort-includes')
144 command
.extend(lines
)
145 command
.extend(['-style=file', '-fallback-style=none'])
146 p
= subprocess
.Popen(command
, stdout
=subprocess
.PIPE
,
147 stderr
=None, stdin
=subprocess
.PIPE
)
148 stdout
, stderr
= p
.communicate()
149 if p
.returncode
!= 0:
150 sys
.exit(p
.returncode
)
153 with
open(filename
) as f
:
155 formatted_code
= StringIO
.StringIO(stdout
).readlines()
156 diff
= difflib
.unified_diff(code
, formatted_code
,
158 '(before formatting)', '(after formatting)')
159 diff_string
= string
.join(diff
, '')
160 if len(diff_string
) > 0:
161 sys
.stdout
.write(diff_string
)
163 if __name__
== '__main__':