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