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