Index: git_cl.py |
diff --git a/git_cl.py b/git_cl.py |
index dc351a2123a9f1bb29c2c7cf95806e7b4dcf7d1e..43c9d365bd93c3a0d1dabe1d2d6372fd8a8a297d 100755 |
--- a/git_cl.py |
+++ b/git_cl.py |
@@ -849,80 +849,108 @@ def GetCodereviewSettingsInteractively(): |
class ChangeDescription(object): |
"""Contains a parsed form of the change description.""" |
R_LINE = r'^[ \t]*(TBR|R)[ \t]*=[ \t]*(.*?)[ \t]*$' |
+ BUG_LINE = r'^[ \t]*(BUG)[ \t]*=[ \t]*(.*?)[ \t]*$' |
def __init__(self, description): |
- self._description = (description or '').strip() |
+ self._description_lines = (description or '').strip().splitlines() |
- @property |
- def description(self): |
- return self._description |
+ @property # www.logilab.org/ticket/89786 |
+ def description(self): # pylint: disable=E0202 |
+ return '\n'.join(self._description_lines) |
+ |
+ def set_description(self, desc): |
+ if isinstance(desc, basestring): |
+ lines = desc.splitlines() |
+ else: |
+ lines = [line.rstrip() for line in desc] |
+ while lines and not lines[0]: |
+ lines.pop(0) |
+ while lines and not lines[-1]: |
+ lines.pop(-1) |
+ self._description_lines = lines |
def update_reviewers(self, reviewers): |
- """Rewrites the R=/TBR= line(s) as a single line.""" |
+ """Rewrites the R=/TBR= line(s) as a single line each.""" |
assert isinstance(reviewers, list), reviewers |
if not reviewers: |
return |
- regexp = re.compile(self.R_LINE, re.MULTILINE) |
- matches = list(regexp.finditer(self._description)) |
- is_tbr = any(m.group(1) == 'TBR' for m in matches) |
- if len(matches) > 1: |
- # Erase all except the first one. |
- for i in xrange(len(matches) - 1, 0, -1): |
- self._description = ( |
- self._description[:matches[i].start()] + |
- self._description[matches[i].end():]) |
- |
- if is_tbr: |
- new_r_line = 'TBR=' + ', '.join(reviewers) |
- else: |
- new_r_line = 'R=' + ', '.join(reviewers) |
- |
- if matches: |
- self._description = ( |
- self._description[:matches[0].start()] + new_r_line + |
- self._description[matches[0].end():]).strip() |
+ reviewers = reviewers[:] |
+ |
+ # Get the set of R= and TBR= lines and remove them from the desciption. |
+ regexp = re.compile(self.R_LINE) |
+ matches = [regexp.match(line) for line in self._description_lines] |
+ new_desc = [l for i, l in enumerate(self._description_lines) |
+ if not matches[i]] |
+ self.set_description(new_desc) |
+ |
+ # Construct new unified R= and TBR= lines. |
+ r_names = [] |
+ tbr_names = [] |
+ for match in matches: |
+ if not match: |
+ continue |
+ people = cleanup_list([match.group(2).strip()]) |
+ if match.group(1) == 'TBR': |
+ tbr_names.extend(people) |
+ else: |
+ r_names.extend(people) |
+ for name in r_names: |
+ if name not in reviewers: |
+ reviewers.append(name) |
+ new_r_line = 'R=' + ', '.join(reviewers) if reviewers else None |
+ new_tbr_line = 'TBR=' + ', '.join(tbr_names) if tbr_names else None |
+ |
+ # Put the new lines in the description where the old first R= line was. |
+ line_loc = next((i for i, match in enumerate(matches) if match), -1) |
+ if 0 <= line_loc < len(self._description_lines): |
+ if new_tbr_line: |
+ self._description_lines.insert(line_loc, new_tbr_line) |
+ if new_r_line: |
+ self._description_lines.insert(line_loc, new_r_line) |
else: |
- self.append_footer(new_r_line) |
+ if new_r_line: |
+ self.append_footer(new_r_line) |
+ if new_tbr_line: |
+ self.append_footer(new_tbr_line) |
def prompt(self): |
"""Asks the user to update the description.""" |
- self._description = ( |
- '# Enter a description of the change.\n' |
- '# This will be displayed on the codereview site.\n' |
- '# The first line will also be used as the subject of the review.\n' |
+ self.set_description([ |
+ '# Enter a description of the change.', |
+ '# This will be displayed on the codereview site.', |
+ '# The first line will also be used as the subject of the review.', |
'#--------------------This line is 72 characters long' |
- '--------------------\n' |
- ) + self._description |
+ '--------------------', |
+ ] + self._description_lines) |
- if '\nBUG=' not in self._description: |
+ regexp = re.compile(self.BUG_LINE) |
+ if not any((regexp.match(line) for line in self._description_lines)): |
self.append_footer('BUG=') |
- content = gclient_utils.RunEditor(self._description, True, |
+ content = gclient_utils.RunEditor(self.description, True, |
git_editor=settings.GetGitEditor()) |
if not content: |
DieWithError('Running editor failed') |
+ lines = content.splitlines() |
# Strip off comments. |
- content = re.compile(r'^#.*$', re.MULTILINE).sub('', content).strip() |
- if not content: |
+ clean_lines = [line.rstrip() for line in lines if not line.startswith('#')] |
+ if not clean_lines: |
DieWithError('No CL description, aborting') |
- self._description = content |
+ self.set_description(clean_lines) |
def append_footer(self, line): |
- # Adds a LF if the last line did not have 'FOO=BAR' or if the new one isn't. |
- if self._description: |
- if '\n' not in self._description: |
- self._description += '\n' |
- else: |
- last_line = self._description.rsplit('\n', 1)[1] |
- if (not presubmit_support.Change.TAG_LINE_RE.match(last_line) or |
- not presubmit_support.Change.TAG_LINE_RE.match(line)): |
- self._description += '\n' |
- self._description += '\n' + line |
+ if self._description_lines: |
+ # Add an empty line if either the last line or the new line isn't a tag. |
+ last_line = self._description_lines[-1] |
+ if (not presubmit_support.Change.TAG_LINE_RE.match(last_line) or |
+ not presubmit_support.Change.TAG_LINE_RE.match(line)): |
+ self._description_lines.append('') |
+ self._description_lines.append(line) |
def get_reviewers(self): |
"""Retrieves the list of reviewers.""" |
- regexp = re.compile(self.R_LINE, re.MULTILINE) |
- reviewers = [i.group(2).strip() for i in regexp.finditer(self._description)] |
+ matches = [re.match(self.R_LINE, line) for line in self._description_lines] |
+ reviewers = [match.group(2).strip() for match in matches if match] |
return cleanup_list(reviewers) |