Changeset - 58bcaf1b2fe2
[Not reviewed]
codereview
0 1 0
Marcin Kuzminski - 13 years ago 2012-05-27 23:28:22
marcin@python-works.com
added stats of line changes and operation (A/M/D) into diffs lib
1 file changed with 18 insertions and 9 deletions:
0 comments (0 inline, 0 general)
rhodecode/lib/diffs.py
Show inline comments
 
@@ -162,47 +162,51 @@ class DiffProcessor(object):
 

	
 
    def copy_iterator(self):
 
        """
 
        make a fresh copy of generator, we should not iterate thru
 
        an original as it's needed for repeating operations on
 
        this instance of DiffProcessor
 
        """
 
        self.__udiff, iterator_copy = tee(self.__udiff)
 
        return iterator_copy
 

	
 
    def _extract_rev(self, line1, line2):
 
        """
 
        Extract the filename and revision hint from a line.
 
        Extract the operation (A/M/D), filename and revision hint from a line.
 
        """
 

	
 
        try:
 
            if line1.startswith('--- ') and line2.startswith('+++ '):
 
                l1 = line1[4:].split(None, 1)
 
                old_filename = (l1[0].replace('a/', '', 1)
 
                                if len(l1) >= 1 else None)
 
                old_rev = l1[1] if len(l1) == 2 else 'old'
 

	
 
                l2 = line2[4:].split(None, 1)
 
                new_filename = (l2[0].replace('b/', '', 1)
 
                                if len(l1) >= 1 else None)
 
                new_rev = l2[1] if len(l2) == 2 else 'new'
 

	
 
                filename = (old_filename
 
                            if old_filename != '/dev/null' else new_filename)
 

	
 
                return filename, new_rev, old_rev
 
                operation = 'D' if new_filename == '/dev/null' else None
 
                if not operation:
 
                    operation = 'M' if old_filename != '/dev/null' else 'A'
 

	
 
                return operation, filename, new_rev, old_rev
 
        except (ValueError, IndexError):
 
            pass
 

	
 
        return None, None, None
 
        return None, None, None, None
 

	
 
    def _parse_gitdiff(self, diffiterator):
 
        def line_decoder(l):
 
            if l.startswith('+') and not l.startswith('+++'):
 
                self.adds += 1
 
            elif l.startswith('-') and not l.startswith('---'):
 
                self.removes += 1
 
            return l.decode('utf8', 'replace')
 

	
 
        output = list(diffiterator)
 
        size = len(output)
 

	
 
@@ -279,42 +283,47 @@ class DiffProcessor(object):
 
            do(next_)
 

	
 
    def _parse_udiff(self):
 
        """
 
        Parse the diff an return data for the template.
 
        """
 
        lineiter = self.lines
 
        files = []
 
        try:
 
            line = lineiter.next()
 
            # skip first context
 
            skipfirst = True
 

	
 
            while 1:
 
                # continue until we found the old file
 
                if not line.startswith('--- '):
 
                    line = lineiter.next()
 
                    continue
 

	
 
                chunks = []
 
                filename, old_rev, new_rev = \
 
                stats = [0, 0]
 
                operation, filename, old_rev, new_rev = \
 
                    self._extract_rev(line, lineiter.next())
 
                files.append({
 
                    'filename':         filename,
 
                    'old_revision':     old_rev,
 
                    'new_revision':     new_rev,
 
                    'chunks':           chunks
 
                    'chunks':           chunks,
 
                    'operation':        operation,
 
                    'stats':            stats,
 
                })
 

	
 
                line = lineiter.next()
 
                while line:
 

	
 
                    match = self._chunk_re.match(line)
 
                    if not match:
 
                        break
 

	
 
                    lines = []
 
                    chunks.append(lines)
 

	
 
                    old_line, old_end, new_line, new_end = \
 
                        [int(x or 1) for x in match.groups()[:-1]]
 
                    old_line -= 1
 
                    new_line -= 1
 
                    context = len(match.groups()) == 5
 
@@ -337,61 +346,60 @@ class DiffProcessor(object):
 
                        if line:
 
                            command, line = line[0], line[1:]
 
                        else:
 
                            command = ' '
 
                        affects_old = affects_new = False
 

	
 
                        # ignore those if we don't expect them
 
                        if command in '#@':
 
                            continue
 
                        elif command == '+':
 
                            affects_new = True
 
                            action = 'add'
 
                            stats[0] += 1
 
                        elif command == '-':
 
                            affects_old = True
 
                            action = 'del'
 
                            stats[1] += 1
 
                        else:
 
                            affects_old = affects_new = True
 
                            action = 'unmod'
 

	
 
                        old_line += affects_old
 
                        new_line += affects_new
 
                        lines.append({
 
                            'old_lineno':   affects_old and old_line or '',
 
                            'new_lineno':   affects_new and new_line or '',
 
                            'action':       action,
 
                            'line':         line
 
                        })
 
                        line = lineiter.next()
 

	
 
        except StopIteration:
 
            pass
 

	
 
        # highlight inline changes
 
        for _ in files:
 
            for chunk in chunks:
 
                lineiter = iter(chunk)
 
                #first = True
 
                try:
 
                    while 1:
 
                        line = lineiter.next()
 
                        if line['action'] != 'unmod':
 
                            nextline = lineiter.next()
 
                            if nextline['action'] == 'unmod' or \
 
                               nextline['action'] == line['action']:
 
                                continue
 
                            self.differ(line, nextline)
 
                except StopIteration:
 
                    pass
 

	
 
        return files
 

	
 
    def prepare(self):
 
        """
 
        Prepare the passed udiff for HTML rendering. It'l return a list
 
        of dicts
 
        """
 
        return self._parse_udiff()
 

	
 
    def _safe_id(self, idstring):
 
        """Make a string safe for including in an id attribute.
 

	
 
@@ -415,40 +423,41 @@ class DiffProcessor(object):
 

	
 
    def raw_diff(self):
 
        """
 
        Returns raw string as udiff
 
        """
 
        udiff_copy = self.copy_iterator()
 
        if self.__format == 'gitdiff':
 
            udiff_copy = self._parse_gitdiff(udiff_copy)
 
        return u''.join(udiff_copy)
 

	
 
    def as_html(self, table_class='code-difftable', line_class='line',
 
                new_lineno_class='lineno old', old_lineno_class='lineno new',
 
                code_class='code', enable_comments=False):
 
                code_class='code', enable_comments=False, diff_lines=None):
 
        """
 
        Return udiff as html table with customized css classes
 
        """
 
        def _link_to_if(condition, label, url):
 
            """
 
            Generates a link if condition is meet or just the label if not.
 
            """
 

	
 
            if condition:
 
                return '''<a href="%(url)s">%(label)s</a>''' % {
 
                    'url': url,
 
                    'label': label
 
                }
 
            else:
 
                return label
 
        if diff_lines is None:
 
        diff_lines = self.prepare()
 
        _html_empty = True
 
        _html = []
 
        _html.append('''<table class="%(table_class)s">\n''' % {
 
            'table_class': table_class
 
        })
 
        for diff in diff_lines:
 
            for line in diff['chunks']:
 
                _html_empty = False
 
                for change in line:
 
                    _html.append('''<tr class="%(lc)s %(action)s">\n''' % {
 
                        'lc': line_class,
0 comments (0 inline, 0 general)