ddea94e8e138 webpy-sucks

Refactor the diff-annotating functionality into the API.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Wed, 03 Mar 2010 18:55:40 -0500
parents f936bf37db17
children 85b4ae69ded2
branches/tags webpy-sucks
files review/api.py review/extension_ui.py

Changes

--- a/review/api.py	Wed Mar 03 18:04:53 2010 -0500
+++ b/review/api.py	Wed Mar 03 18:55:40 2010 -0500
@@ -369,6 +369,7 @@
             self.node, filename, lines, message)
         comment._commit(self.ui, self.repo)
     
+    
     def full_diffs(self, filenames=None, opts={}):
         """Return full diffs of the given files (or all files).
         
@@ -402,7 +403,7 @@
         if not filenames:
             filenames = target_files
         else:
-            filenames = filter(lambda f: f in target_files, filenames)
+            filenames = filter(lambda f: self.has_diff(f), filenames)
         
         opts['unified'] = '100000'
         node2 = self.node
@@ -457,8 +458,8 @@
         
         There's a lot of structure there, but it will provide everything you
         need to display contextualized diffs.
+        """
         
-        """
         ds = self.full_diffs(filenames, {})
         
         def _filter_diff(d):
@@ -477,6 +478,83 @@
         
         return ds
     
+    def annotated_diff(self, filename, context=5):
+        """Return a generator that yields annotated lines of a diff.
+        
+        The first item yielded will be a simple integer of the last line
+        number of the diff.  This is ugly but useful when creating monospaced
+        line-number-prefixed output.
+        
+        Each line yielded will be of the form:
+        
+        {
+            # If 'skipped' is not None, this line is a "skip" line, which
+            # represents a group of lines that were skipped due to context.
+            'skipped': 23,
+            
+            # The line number of this line, or None for skip lines.
+            'number': 585,
+            
+            # The actual content of this line, or None for skip lines.
+            'content': '+added line',
+            
+            # Any comments that apply to this line.
+            # If the line is a skip line, this will be any comments that apply
+            # to any line in the skipped group.
+            'comments': [ReviewComment(), ReviewComment()],
+        }
+        """
+        
+        diffs = self.diffs([filename], context).values()
+        if not diffs:
+            return
+        
+        diff = diffs[0]
+        max_line, content = diff['max'], diff['content']
+        line_level_comments = self.line_level_comments(filename)
+        previous_n = -1
+        
+        yield content[-1][0]
+        
+        for n, line in content:
+            if n - 1 > previous_n:
+                yield {
+                    'skipped': (n - previous_n) - (previous_n == -1 and 1 or 0),
+                    'number': None, 'content': None,
+                    'comments': filter(
+                        lambda c: max(c.lines) in range(previous_n + 1, n),
+                        line_level_comments
+                    ),
+                }
+            
+            yield {
+                'skipped': None,
+                'number': n, 'content': line,
+                'comments': filter(
+                    lambda c: max(c.lines) == n, line_level_comments
+                )
+            }
+            
+            previous_n = n
+        
+        if previous_n < max_line:
+            yield {
+                'skipped': max_line - previous_n,
+                'number': None, 'content': None,
+                'comments': filter(
+                    lambda c: max(c.lines) in range(previous_n + 1, max_line),
+                    line_level_comments
+                ),
+            }
+    
+    def has_diff(self, filename):
+        """Return whether the given filename has a diff in this revision."""
+        return filename in self.files()
+    
+    def files(self):
+        """Return the list of files in the revision for this ReviewChangeset."""
+        return self.target[self.node].files()
+    
     
     def review_level_comments(self):
         """Comments on this changeset which aren't on a particular file."""
--- a/review/extension_ui.py	Wed Mar 03 18:04:53 2010 -0500
+++ b/review/extension_ui.py	Wed Mar 03 18:55:40 2010 -0500
@@ -147,6 +147,7 @@
         
         ui.write(after)
     
+    
     if rcset.signoffs:
         ui.write('\n')
     for signoff in rcset.signoffs:
@@ -161,53 +162,31 @@
     if ui.quiet:
         return
     
+    if not fnames:
+        fnames = rcset.files()
     fnames = [sanitize_path(fname, repo) for fname in fnames]
-    diffs = rcset.diffs(fnames, context)
+    fnames = [fname for fname in fnames if rcset.has_diff(fname)]
     
-    for filename, diff in diffs.iteritems():
-        max_line = diff['max']
-        content = diff['content']
-        
+    for filename in fnames:
         header = messages.REVIEW_LOG_FILE_HEADER % filename
         print '\n\n%s %s' % (header, '-'*(80-(len(header)+1)))
         
         for comment in rcset.file_level_comments(filename):
             _print_comment(comment)
         
-        line_level_comments = rcset.line_level_comments(filename)
-        prefix = '%%%dd: ' % len(str(content[-1][0]))
-        previous_n = -1
-        for n, line in content:
-            if n - 1 > previous_n:
-                skipped = n - previous_n
-                if previous_n == -1:
-                    skipped -= 1
-                ui.write(messages.REVIEW_LOG_SKIPPED % skipped)
-                skipped_comments = filter(
-                    lambda c: max(c.lines) in range(previous_n + 1, n),
-                    line_level_comments
-                )
-                for comment in skipped_comments:
+        annotated_diff = rcset.annotated_diff(filename, context)
+        prefix = '%%%dd: ' % len(str(annotated_diff.next()))
+        
+        for line in annotated_diff:
+            if line['skipped']:
+                ui.write(messages.REVIEW_LOG_SKIPPED % line['skipped'])
+                for comment in line['comments']:
                     _print_comment(comment)
+                continue
             
-            ui.write('%s %s\n' % (prefix % n, line))
-            
-            line_comments = filter(
-                lambda c: max(c.lines) == n, line_level_comments
-            )
-            for comment in line_comments:
-                _print_comment(comment)
+            ui.write('%s %s\n' % (prefix % line['number'], line['content']))
             
-            previous_n = n
-        
-        if previous_n < max_line:
-            skipped = max_line - previous_n
-            ui.write(messages.REVIEW_LOG_SKIPPED % skipped)
-            skipped_comments = filter(
-                lambda c: max(c.lines) in range(previous_n + 1, max_line),
-                line_level_comments
-            )
-            for comment in skipped_comments:
+            for comment in line['comments']:
                 _print_comment(comment)