1233d2500723e5594f3e7c70896ffeeef32b9c950ywan#!/usr/bin/env python 2233d2500723e5594f3e7c70896ffeeef32b9c950ywan## Copyright (c) 2012 The WebM project authors. All Rights Reserved. 3233d2500723e5594f3e7c70896ffeeef32b9c950ywan## 4233d2500723e5594f3e7c70896ffeeef32b9c950ywan## Use of this source code is governed by a BSD-style license 5233d2500723e5594f3e7c70896ffeeef32b9c950ywan## that can be found in the LICENSE file in the root of the source 6233d2500723e5594f3e7c70896ffeeef32b9c950ywan## tree. An additional intellectual property rights grant can be found 7233d2500723e5594f3e7c70896ffeeef32b9c950ywan## in the file PATENTS. All contributing project authors may 8233d2500723e5594f3e7c70896ffeeef32b9c950ywan## be found in the AUTHORS file in the root of the source tree. 9233d2500723e5594f3e7c70896ffeeef32b9c950ywan## 10233d2500723e5594f3e7c70896ffeeef32b9c950ywan"""Classes for representing diff pieces.""" 11233d2500723e5594f3e7c70896ffeeef32b9c950ywan 12233d2500723e5594f3e7c70896ffeeef32b9c950ywan__author__ = "jkoleszar@google.com" 13233d2500723e5594f3e7c70896ffeeef32b9c950ywan 14233d2500723e5594f3e7c70896ffeeef32b9c950ywanimport re 15233d2500723e5594f3e7c70896ffeeef32b9c950ywan 16233d2500723e5594f3e7c70896ffeeef32b9c950ywan 17233d2500723e5594f3e7c70896ffeeef32b9c950ywanclass DiffLines(object): 18233d2500723e5594f3e7c70896ffeeef32b9c950ywan """A container for one half of a diff.""" 19233d2500723e5594f3e7c70896ffeeef32b9c950ywan 20233d2500723e5594f3e7c70896ffeeef32b9c950ywan def __init__(self, filename, offset, length): 21233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.filename = filename 22233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.offset = offset 23233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.length = length 24233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.lines = [] 25233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.delta_line_nums = [] 26233d2500723e5594f3e7c70896ffeeef32b9c950ywan 27233d2500723e5594f3e7c70896ffeeef32b9c950ywan def Append(self, line): 28233d2500723e5594f3e7c70896ffeeef32b9c950ywan l = len(self.lines) 29233d2500723e5594f3e7c70896ffeeef32b9c950ywan if line[0] != " ": 30233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.delta_line_nums.append(self.offset + l) 31233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.lines.append(line[1:]) 32233d2500723e5594f3e7c70896ffeeef32b9c950ywan assert l+1 <= self.length 33233d2500723e5594f3e7c70896ffeeef32b9c950ywan 34233d2500723e5594f3e7c70896ffeeef32b9c950ywan def Complete(self): 35233d2500723e5594f3e7c70896ffeeef32b9c950ywan return len(self.lines) == self.length 36233d2500723e5594f3e7c70896ffeeef32b9c950ywan 37233d2500723e5594f3e7c70896ffeeef32b9c950ywan def __contains__(self, item): 38233d2500723e5594f3e7c70896ffeeef32b9c950ywan return item >= self.offset and item <= self.offset + self.length - 1 39233d2500723e5594f3e7c70896ffeeef32b9c950ywan 40233d2500723e5594f3e7c70896ffeeef32b9c950ywan 41233d2500723e5594f3e7c70896ffeeef32b9c950ywanclass DiffHunk(object): 42233d2500723e5594f3e7c70896ffeeef32b9c950ywan """A container for one diff hunk, consisting of two DiffLines.""" 43233d2500723e5594f3e7c70896ffeeef32b9c950ywan 44233d2500723e5594f3e7c70896ffeeef32b9c950ywan def __init__(self, header, file_a, file_b, start_a, len_a, start_b, len_b): 45233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.header = header 46233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.left = DiffLines(file_a, start_a, len_a) 47233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.right = DiffLines(file_b, start_b, len_b) 48233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.lines = [] 49233d2500723e5594f3e7c70896ffeeef32b9c950ywan 50233d2500723e5594f3e7c70896ffeeef32b9c950ywan def Append(self, line): 51233d2500723e5594f3e7c70896ffeeef32b9c950ywan """Adds a line to the DiffHunk and its DiffLines children.""" 52233d2500723e5594f3e7c70896ffeeef32b9c950ywan if line[0] == "-": 53233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.left.Append(line) 54233d2500723e5594f3e7c70896ffeeef32b9c950ywan elif line[0] == "+": 55233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.right.Append(line) 56233d2500723e5594f3e7c70896ffeeef32b9c950ywan elif line[0] == " ": 57233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.left.Append(line) 58233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.right.Append(line) 59233d2500723e5594f3e7c70896ffeeef32b9c950ywan elif line[0] == "\\": 60233d2500723e5594f3e7c70896ffeeef32b9c950ywan # Ignore newline messages from git diff. 61233d2500723e5594f3e7c70896ffeeef32b9c950ywan pass 62233d2500723e5594f3e7c70896ffeeef32b9c950ywan else: 63233d2500723e5594f3e7c70896ffeeef32b9c950ywan assert False, ("Unrecognized character at start of diff line " 64233d2500723e5594f3e7c70896ffeeef32b9c950ywan "%r" % line[0]) 65233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.lines.append(line) 66233d2500723e5594f3e7c70896ffeeef32b9c950ywan 67233d2500723e5594f3e7c70896ffeeef32b9c950ywan def Complete(self): 68233d2500723e5594f3e7c70896ffeeef32b9c950ywan return self.left.Complete() and self.right.Complete() 69233d2500723e5594f3e7c70896ffeeef32b9c950ywan 70233d2500723e5594f3e7c70896ffeeef32b9c950ywan def __repr__(self): 71233d2500723e5594f3e7c70896ffeeef32b9c950ywan return "DiffHunk(%s, %s, len %d)" % ( 72233d2500723e5594f3e7c70896ffeeef32b9c950ywan self.left.filename, self.right.filename, 73233d2500723e5594f3e7c70896ffeeef32b9c950ywan max(self.left.length, self.right.length)) 74233d2500723e5594f3e7c70896ffeeef32b9c950ywan 75233d2500723e5594f3e7c70896ffeeef32b9c950ywan 76233d2500723e5594f3e7c70896ffeeef32b9c950ywandef ParseDiffHunks(stream): 77233d2500723e5594f3e7c70896ffeeef32b9c950ywan """Walk a file-like object, yielding DiffHunks as they're parsed.""" 78233d2500723e5594f3e7c70896ffeeef32b9c950ywan 79233d2500723e5594f3e7c70896ffeeef32b9c950ywan file_regex = re.compile(r"(\+\+\+|---) (\S+)") 80233d2500723e5594f3e7c70896ffeeef32b9c950ywan range_regex = re.compile(r"@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))?") 81233d2500723e5594f3e7c70896ffeeef32b9c950ywan hunk = None 82233d2500723e5594f3e7c70896ffeeef32b9c950ywan while True: 83233d2500723e5594f3e7c70896ffeeef32b9c950ywan line = stream.readline() 84233d2500723e5594f3e7c70896ffeeef32b9c950ywan if not line: 85233d2500723e5594f3e7c70896ffeeef32b9c950ywan break 86233d2500723e5594f3e7c70896ffeeef32b9c950ywan 87233d2500723e5594f3e7c70896ffeeef32b9c950ywan if hunk is None: 88233d2500723e5594f3e7c70896ffeeef32b9c950ywan # Parse file names 89233d2500723e5594f3e7c70896ffeeef32b9c950ywan diff_file = file_regex.match(line) 90233d2500723e5594f3e7c70896ffeeef32b9c950ywan if diff_file: 91233d2500723e5594f3e7c70896ffeeef32b9c950ywan if line.startswith("---"): 92233d2500723e5594f3e7c70896ffeeef32b9c950ywan a_line = line 93233d2500723e5594f3e7c70896ffeeef32b9c950ywan a = diff_file.group(2) 94233d2500723e5594f3e7c70896ffeeef32b9c950ywan continue 95233d2500723e5594f3e7c70896ffeeef32b9c950ywan if line.startswith("+++"): 96233d2500723e5594f3e7c70896ffeeef32b9c950ywan b_line = line 97233d2500723e5594f3e7c70896ffeeef32b9c950ywan b = diff_file.group(2) 98233d2500723e5594f3e7c70896ffeeef32b9c950ywan continue 99233d2500723e5594f3e7c70896ffeeef32b9c950ywan 100233d2500723e5594f3e7c70896ffeeef32b9c950ywan # Parse offset/lengths 101233d2500723e5594f3e7c70896ffeeef32b9c950ywan diffrange = range_regex.match(line) 102233d2500723e5594f3e7c70896ffeeef32b9c950ywan if diffrange: 103233d2500723e5594f3e7c70896ffeeef32b9c950ywan if diffrange.group(2): 104233d2500723e5594f3e7c70896ffeeef32b9c950ywan start_a = int(diffrange.group(1)) 105233d2500723e5594f3e7c70896ffeeef32b9c950ywan len_a = int(diffrange.group(3)) 106233d2500723e5594f3e7c70896ffeeef32b9c950ywan else: 107233d2500723e5594f3e7c70896ffeeef32b9c950ywan start_a = 1 108233d2500723e5594f3e7c70896ffeeef32b9c950ywan len_a = int(diffrange.group(1)) 109233d2500723e5594f3e7c70896ffeeef32b9c950ywan 110233d2500723e5594f3e7c70896ffeeef32b9c950ywan if diffrange.group(5): 111233d2500723e5594f3e7c70896ffeeef32b9c950ywan start_b = int(diffrange.group(4)) 112233d2500723e5594f3e7c70896ffeeef32b9c950ywan len_b = int(diffrange.group(6)) 113233d2500723e5594f3e7c70896ffeeef32b9c950ywan else: 114233d2500723e5594f3e7c70896ffeeef32b9c950ywan start_b = 1 115233d2500723e5594f3e7c70896ffeeef32b9c950ywan len_b = int(diffrange.group(4)) 116233d2500723e5594f3e7c70896ffeeef32b9c950ywan 117233d2500723e5594f3e7c70896ffeeef32b9c950ywan header = [a_line, b_line, line] 118233d2500723e5594f3e7c70896ffeeef32b9c950ywan hunk = DiffHunk(header, a, b, start_a, len_a, start_b, len_b) 119233d2500723e5594f3e7c70896ffeeef32b9c950ywan else: 120233d2500723e5594f3e7c70896ffeeef32b9c950ywan # Add the current line to the hunk 121233d2500723e5594f3e7c70896ffeeef32b9c950ywan hunk.Append(line) 122233d2500723e5594f3e7c70896ffeeef32b9c950ywan 123233d2500723e5594f3e7c70896ffeeef32b9c950ywan # See if the whole hunk has been parsed. If so, yield it and prepare 124233d2500723e5594f3e7c70896ffeeef32b9c950ywan # for the next hunk. 125233d2500723e5594f3e7c70896ffeeef32b9c950ywan if hunk.Complete(): 126233d2500723e5594f3e7c70896ffeeef32b9c950ywan yield hunk 127233d2500723e5594f3e7c70896ffeeef32b9c950ywan hunk = None 128233d2500723e5594f3e7c70896ffeeef32b9c950ywan 129233d2500723e5594f3e7c70896ffeeef32b9c950ywan # Partial hunks are a parse error 130233d2500723e5594f3e7c70896ffeeef32b9c950ywan assert hunk is None 131