7e437c5261bb

Output diffs (with line numbers) and review-level comments for review.

Parsing and formatting unified diffs is hard, let's ride bikes^H^H^H^H^H^H^H^H^H^H^H drink beer!
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Mon, 05 Oct 2009 19:45:45 -0400
parents 60a287538a94
children 1ef154bb1a4f
branches/tags (none)
files review/api.py review/extension_ui.py review/messages.py review/tests/test_comment.py

Changes

--- a/review/api.py	Sun Oct 04 22:25:33 2009 -0400
+++ b/review/api.py	Mon Oct 05 19:45:45 2009 -0400
@@ -6,7 +6,7 @@
 import os
 import messages, templates
 from datetime import datetime
-from mercurial import cmdutil, hg
+from mercurial import cmdutil, hg, patch
 from mercurial.node import hex
 from mercurial.util import sha1
 
@@ -90,13 +90,14 @@
     def __getitem__(self, rev):
         '''Return a ReviewChangeset for the given revision.'''
         node = hex(self.target[rev].node())
-        return ReviewChangeset(self.ui, self.repo, node)
+        return ReviewChangeset(self.ui, self.repo, self.target, node)
     
 
 class ReviewChangeset(object):
     '''The review data about one changeset in the target repository.'''
-    def __init__(self, ui, repo, node):
+    def __init__(self, ui, repo, target, node):
         self.repo = repo
+        self.target = target
         self.ui = ui
         self.node = node
         
@@ -146,6 +147,34 @@
             self.node, filename, lines, message)
         comment.commit(self.ui, self.repo)
     
+    def full_diffs(self, filenames=None, opts={}):
+        '''Return diffs of the given files.'''
+        
+        target_files = self.target[self.node].files()
+        if not filenames:
+            filenames = target_files
+        else:
+            filenames = filter(lambda f: f in target_files, filenames)
+        
+        opts['unified'] = '100000'
+        node2 = self.node
+        node1 = self.target[node2].parents()[0].node()
+        
+        diffs = {}
+        for filename in filenames:
+            m = cmdutil.matchfiles(self.target, [filename])
+            d = patch.diff(self.target, node1, node2, match=m,
+                opts=patch.diffopts(self.ui, opts))
+            
+            # patch.diff will give us back a generator with two items
+            # the first is the diff header, which we don't care about
+            d.next()
+            
+            # the second is the diff's contents, which is what we want
+            diffs[filename] = d.next()
+        
+        return diffs
+    
 
 class _ReviewObject(object):
     '''Some kind of object.'''
--- a/review/extension_ui.py	Sun Oct 04 22:25:33 2009 -0400
+++ b/review/extension_ui.py	Mon Oct 05 19:45:45 2009 -0400
@@ -51,7 +51,7 @@
     
     return
 
-def _review_command(ui, repo, **opts):
+def _review_command(ui, repo, *fnames, **opts):
     rev = opts.pop('rev')
     rd = ReviewDatastore(ui, repo)
     cset = repo[rev]
@@ -66,6 +66,26 @@
     
     ui.write(messages.REVIEW_LOG_SIGNOFFS % len(rcset.signoffs))
     ui.write(messages.REVIEW_LOG_COMMENTS % (comment_count, author_count))
+    
+    review_level_comments = filter(lambda c: not c.filename, rcset.comments)
+    if review_level_comments:
+        ui.write('\n')
+    for comment in review_level_comments:
+        ui.write('\n' + messages.REVIEW_LOG_COMMENT_AUTHOR % comment.author)
+        for line in comment.message.splitlines():
+            ui.write(messages.REVIEW_LOG_COMMENT_LINE % line)
+    
+    diffs = rcset.full_diffs(fnames, opts)
+    
+    for filename in diffs:
+        header = messages.REVIEW_LOG_FILE_HEADER % filename
+        print '\n\n%s %s' % (header, '-'*(80-(len(header)+1)))
+        
+        content = diffs[filename].splitlines()[2:]
+        
+        prefix = '%%%dd: ' % len(str(len(content)-1))
+        for n, line in enumerate(content):
+            ui.write('%s %s\n' % (prefix % n, line))
 
 
 def review(ui, repo, *fnames, **opts):
@@ -78,7 +98,7 @@
     elif opts.pop('signoff'):
         return _signoff_command(ui, repo, **opts)
     else:
-        return _review_command(ui, repo, **opts)
+        return _review_command(ui, repo, *fnames, **opts)
 
 
 cmdtable = {
--- a/review/messages.py	Sun Oct 04 22:25:33 2009 -0400
+++ b/review/messages.py	Mon Oct 05 19:45:45 2009 -0400
@@ -53,9 +53,17 @@
 
 REVIEW_LOG_COMMENTS = '''\
 comments:  %d comments from %d authors
-
 '''
 
+REVIEW_LOG_FILE_HEADER = '''changes in %s'''
+
+REVIEW_LOG_COMMENT_AUTHOR = '''\
+# %s said:
+'''
+
+REVIEW_LOG_COMMENT_LINE = '''\
+#     %s
+'''
 
 COMMIT_COMMENT = '''Add a comment on changeset %s'''
 COMMIT_SIGNOFF = '''Sign off on changeset %s'''
--- a/review/tests/test_comment.py	Sun Oct 04 22:25:33 2009 -0400
+++ b/review/tests/test_comment.py	Mon Oct 05 19:45:45 2009 -0400
@@ -36,10 +36,19 @@
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (1, 1) in output
     
+    author_line = messages.REVIEW_LOG_COMMENT_AUTHOR % '|'
+    a1, _, a2 = author_line.partition('|')
+    assert a1 in output
+    assert a2 in output
+    
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
+    
     review(comment=True, message='Test comment two.')
     
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (2, 1) in output
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment two.' in output
 
 
 @with_setup(setup_reviewed_sandbox, teardown_sandbox)
@@ -51,11 +60,21 @@
     output = review(rev='0')
     assert messages.REVIEW_LOG_COMMENTS % (1, 1) in output
     
+    author_line = messages.REVIEW_LOG_COMMENT_AUTHOR % '|'
+    a1, _, a2 = author_line.partition('|')
+    assert a1 in output
+    assert a2 in output
+    
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
+    
     output = review()
     assert messages.REVIEW_LOG_COMMENTS % (0, 0) in output
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' not in output
     
     review(comment=True, message='Test comment two.', rev='0')
     
     output = review(rev='0')
     assert messages.REVIEW_LOG_COMMENTS % (2, 1) in output
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment one.' in output
+    assert messages.REVIEW_LOG_COMMENT_LINE % 'Test comment two.' in output