OLD | NEW |
| (Empty) |
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 # Copyright 2008 Google Inc. All Rights Reserved. | |
6 # | |
7 # Licensed under the Apache License, Version 2.0 (the "License"); | |
8 # you may not use this file except in compliance with the License. | |
9 # You may obtain a copy of the License at | |
10 # | |
11 # http://www.apache.org/licenses/LICENSE-2.0 | |
12 # | |
13 # Unless required by applicable law or agreed to in writing, software | |
14 # distributed under the License is distributed on an "AS IS" BASIS, | |
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
16 # See the License for the specific language governing permissions and | |
17 # limitations under the License. | |
18 | |
19 """Utility for parsing patches, originally inspired by rietveld source.""" | |
20 | |
21 import re | |
22 | |
23 | |
24 _CHUNK_RE = re.compile(r""" | |
25 @@ | |
26 \s+ | |
27 - | |
28 (?: (\d+) (?: , (\d+) )?) | |
29 \s+ | |
30 \+ | |
31 (?: (\d+) (?: , (\d+) )?) | |
32 \s+ | |
33 @@ | |
34 """, re.VERBOSE) | |
35 | |
36 | |
37 class PatchParseError(Exception): | |
38 """Raised on parse errors.""" | |
39 pass | |
40 | |
41 | |
42 def ParsePatchToLines(lines): | |
43 """Parses a patch from an iterable type. | |
44 | |
45 Args: | |
46 lines: The lines to parse. | |
47 | |
48 Returns: | |
49 None on error, otherwise a list of 3-tuples: | |
50 (old_line_no, new_line_no, line) | |
51 | |
52 A line number can be None if it doesn't exist in the old/new file. | |
53 """ | |
54 # Helper function that matches a hunk header and returns line numbers. | |
55 def match_hunk_start(line): | |
56 match = _CHUNK_RE.match(line) | |
57 if not match: | |
58 raise PatchParseError(line) | |
59 return (int(match.groups()[0]), int(match.groups()[2])) | |
60 | |
61 iterator = lines.__iter__() | |
62 try: | |
63 # Skip leading lines until after we've seen one starting with '+++'. | |
64 while not iterator.next().startswith('+++'): | |
65 pass | |
66 | |
67 # Parse first hunk header. | |
68 old_ln, new_ln = match_hunk_start(iterator.next()) | |
69 except StopIteration: | |
70 return [] | |
71 | |
72 # Process the actual patch lines. | |
73 result = [] | |
74 for line in iterator: | |
75 if line[0] == '@': | |
76 old_ln, new_ln = match_hunk_start(line) | |
77 elif line[0] == '-': | |
78 result.append((old_ln, None, line[1:])) | |
79 old_ln += 1 | |
80 elif line[0] == '+': | |
81 result.append((None, new_ln, line[1:])) | |
82 new_ln += 1 | |
83 elif line[0] == ' ': | |
84 result.append((old_ln, new_ln, line[1:])) | |
85 old_ln += 1 | |
86 new_ln += 1 | |
87 else: | |
88 raise PatchParseError(line) | |
89 | |
90 return result | |
OLD | NEW |