Merge #11997: [tests] util_tests.cpp: actually check ignored args
[bitcoinplatinum.git] / contrib / devtools / clang-format-diff.py
blob7ea49b65e1776a00084375c5271a7d1d7ec4d477
1 #!/usr/bin/env python
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
8 # License.
10 # ============================================================
12 # University of Illinois/NCSA
13 # Open Source License
15 # Copyright (c) 2007-2015 University of Illinois at Urbana-Champaign.
16 # All rights reserved.
18 # Developed by:
20 # LLVM Team
22 # University of Illinois at Urbana-Champaign
24 # http://llvm.org
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
51 # SOFTWARE.
53 # ============================================================
55 #===------------------------------------------------------------------------===#
57 r"""
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
68 """
70 import argparse
71 import difflib
72 import re
73 import string
74 import subprocess
75 import StringIO
76 import sys
79 # Change this to the full path if clang-format is not on the path.
80 binary = 'clang-format'
83 def main():
84 parser = argparse.ArgumentParser(description=
85 'Reformat changed lines in diff. Without -i '
86 'option just output the diff that would be '
87 'introduced.')
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'
97 r'|protodevel|java)',
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.
107 filename = None
108 lines_by_file = {}
109 for line in sys.stdin:
110 match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
111 if match:
112 filename = match.group(2)
113 if filename == None:
114 continue
116 if args.regex is not None:
117 if not re.match('^%s$' % args.regex, filename):
118 continue
119 else:
120 if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
121 continue
123 match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
124 if match:
125 start_line = int(match.group(1))
126 line_count = 1
127 if match.group(3):
128 line_count = int(match.group(3))
129 if line_count == 0:
130 continue
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]
140 if args.i:
141 command.append('-i')
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)
152 if not args.i:
153 with open(filename) as f:
154 code = f.readlines()
155 formatted_code = StringIO.StringIO(stdout).readlines()
156 diff = difflib.unified_diff(code, formatted_code,
157 filename, filename,
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__':
164 main()