#!/usr/bin/python # #===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===# # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # #===------------------------------------------------------------------------===# r""" ClangFormat Diff Reformatter ============================ This script reads input from a unified diff and reformats all the changed lines. This is useful to reformat all the lines touched by a specific patch. Example usage for git users: git diff -U0 HEAD^ | clang-format-diff.py -p1 """ import argparse import re import subprocess import sys # Change this to the full path if clang-format is not on the path. binary = 'clang-format' def getOffsetLength(filename, line_number, line_count): """ Calculates the field offset and length based on line number and count. """ offset = 0 length = 0 with open(filename, 'r') as f: for line in f: if line_number > 1: offset += len(line) line_number -= 1 elif line_count > 0: length += len(line) line_count -= 1 else: break return offset, length def formatRange(r, style): """ Formats range 'r' according to style 'style'. """ filename, line_number, line_count = r # FIXME: Add other types containing C++/ObjC code. if not (filename.endswith(".cpp") or filename.endswith(".cc") or filename.endswith(".h")): return offset, length = getOffsetLength(filename, line_number, line_count) with open(filename, 'r') as f: text = f.read() p = subprocess.Popen([binary, '-offset', str(offset), '-length', str(length), '-style', style], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) stdout, stderr = p.communicate(input=text) if stderr: print stderr return if not stdout: print 'Segfault occurred while formatting', filename print 'Please report a bug on llvm.org/bugs.' return with open(filename, 'w') as f: f.write(stdout) def main(): parser = argparse.ArgumentParser(description= 'Reformat changed lines in diff') parser.add_argument('-p', default=1, help='strip the smallest prefix containing P slashes') parser.add_argument('-style', default='LLVM', help='formatting style to apply (LLVM, Google)') args = parser.parse_args() filename = None ranges = [] for line in sys.stdin: match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) if match: filename = match.group(2) if filename == None: continue match = re.search('^@@.*\+(\d+)(,(\d+))?', line) if match: line_count = 1 if match.group(3): line_count = int(match.group(3)) ranges.append((filename, int(match.group(1)), line_count)) # Reverse the ranges so that the reformatting does not influence file offsets. for r in reversed(ranges): # Do the actual formatting. formatRange(r, args.style) if __name__ == '__main__': main()