src/cm/utils/diff.py
author gibus
Wed, 11 Sep 2013 23:13:01 +0200
changeset 532 0bad3613f59d
parent 251 3eb5299e8085
permissions -rw-r--r--
Reverse to YUI 3.0.0 since with YUI.3.10.3, comment content including words 'paragraph' or 'section' do not show up on Firefox, this is weird and has to be investigated.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
149
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     1
import difflib, string
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     2
from django.utils.translation import ugettext as _
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     3
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     4
def isatag(x): return x.startswith("<") and x.endswith(">")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     5
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     6
def text_diff(a, b):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     7
    res = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     8
    a, b = createlist(a), createlist(b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
     9
    s = difflib.SequenceMatcher(isatag, a, b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    10
    for e in s.get_opcodes():
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    11
        if e[0] == "replace":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    12
            res.append('<del class="diff modified">'+''.join(a[e[1]:e[2]]) + '</del><ins class="diff modified">'+''.join(b[e[3]:e[4]])+"</ins>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    13
        elif e[0] == "delete":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    14
            res.append('<del class="diff">'+ ''.join(a[e[1]:e[2]]) + "</del>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    15
        elif e[0] == "insert":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    16
            res.append('<ins class="diff">'+''.join(b[e[3]:e[4]]) + "</ins>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    17
        elif e[0] == "equal":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    18
            res.append(''.join(b[e[3]:e[4]]))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    19
        else: 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    20
            raise "error parsing %s" %(e[0])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    21
    return ''.join(res)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    22
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    23
COLORS = [
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    24
'#FF0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    25
'#EE0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    26
'#DD0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    27
'#CC0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    28
'#BB0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    29
'#AA0000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    30
'#990000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    31
'#880000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    32
'#770000',
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    33
]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    34
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    35
from colorsys import hls_to_rgb, hsv_to_rgb
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    36
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    37
def generatePastelColors(n):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    38
    s = .4
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    39
    v = .99
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    40
    return ['#'+''.join((hex(int(r*255))[2:],hex(int(g*255))[2:],hex(int(b*255))[2:])) for r,g,b in [hsv_to_rgb(h/200.,s,v) for h in range(1,100,100/n)]]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    41
    
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    42
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    43
DEFAULT_COLOR = '#D9D9D9'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    44
def get_colors(authors, last_white = False):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    45
    """
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    46
    returns a dict for authors's colors (with last one white if 'last_white')
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    47
    """
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    48
    authors = set(authors)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    49
    #new_colors = list(tuple(COLORS))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    50
    new_colors = generatePastelColors(len(set(authors)))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    51
    res = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    52
    if None in authors or '' in authors:
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    53
        res = [(_(u'unknown'),DEFAULT_COLOR) ]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    54
    res.extend([(author,new_colors.pop()) for author in authors if author])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    55
    #if authors[-1]:
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    56
    #    res[authors[-1]] = '#FFFFFF'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    57
    return res
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    58
    
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    59
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    60
def text_history(versions, authors):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    61
    res = versions[0]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    62
    colors = get_colors(authors)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    63
    for ind in range(len(versions)-1):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    64
        author = authors[ind]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    65
        color = colors.get(author,DEFAULT_COLOR)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    66
        v_2 = versions[ind + 1]
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    67
        res = text_diff_add(v_2, res, color)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    68
    return res,colors.items()
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    69
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    70
from cm.utils.html import surrond_text_node
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    71
 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    72
def text_diff_add(a,b, color):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    73
    res = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    74
    a, b = createlist(a), createlist(b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    75
    s = difflib.SequenceMatcher(isatag, a, b)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    76
    for e in s.get_opcodes():
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    77
        if e[0] == "replace":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    78
            html_chunk = ''.join(b[e[3]:e[4]])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    79
            new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>')
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    80
            res.append(new_html_chunk)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    81
            #res.append('<font style="background: %s;" class="diff modified">' %color+''.join(b[e[3]:e[4]])+"</font>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    82
        elif e[0] == "delete":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    83
            pass
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    84
        elif e[0] == "insert":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    85
            html_chunk = ''.join(b[e[3]:e[4]])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    86
            new_html_chunk = surrond_text_node(html_chunk,'<span style="background: %s;">' %color,'</span>') 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    87
            res.append(new_html_chunk)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    88
            #res.append('<font style="background: %s;" class="diff">' %color+''.join(b[e[3]:e[4]]) + "</font>")
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    89
        elif e[0] == "equal":
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    90
            res.append(''.join(b[e[3]:e[4]]))
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    91
        else: 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    92
            raise "error parsing %s" %(e[0])
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    93
    return ''.join(res)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    94
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    95
def createlist(x, b=0):
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    96
    mode = 'char'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    97
    cur = ''
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    98
    out = []
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
    99
    for c in x:
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   100
        if mode == 'tag':
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   101
            if c == '>': 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   102
                if b: cur += ']'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   103
                else: cur += c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   104
                out.append(cur); cur = ''; mode = 'char'
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   105
            else: cur += c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   106
        elif mode == 'char':
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   107
            if c == '<': 
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   108
                out.append(cur)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   109
                if b: cur = '['
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   110
                else: cur = c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   111
                mode = 'tag'            
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   112
            elif c in string.whitespace: out.append(cur+c); cur = ''
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   113
            else: cur += c
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   114
    out.append(cur)
0f2c5744b39b cleanup diff files / add experimental diff
raph
parents:
diff changeset
   115
    return filter(lambda x: x is not '', out)
250
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   116
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   117
# 
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   118
251
3eb5299e8085 fix import
raph
parents: 250
diff changeset
   119
from cm.ext.diff_match_patch import diff_match_patch
250
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   120
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   121
class diff_match_patch2(diff_match_patch):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   122
    def diff_prettyHtml_one_way(self, diffs, way=False, mode='red'):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   123
      """Convert a diff array into a pretty HTML report.
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   124
    
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   125
      Args:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   126
        diffs: Array of diff tuples.
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   127
        way: [None, 1, 2]
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   128
        mode: ['ins_del', 'red']
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   129
    
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   130
      Returns:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   131
        HTML representation.
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   132
      """
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   133
      html = []
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   134
      i = 0
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   135
      for (op, data) in diffs:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   136
        text = data #(data.replace("&", "&amp;").replace("<", "&lt;")
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   137
                   #.replace(">", "&gt;").replace("\n", "&para;<BR>"))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   138
        if op == self.DIFF_INSERT and (not way or way==1):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   139
            if mode=='red':
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   140
                html.append('<span class="diffchange-ins">%s</span>' % (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   141
            else:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   142
                html.append('<ins>%s</ins>' % (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   143
                
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   144
        elif op == self.DIFF_DELETE and (not way or way==2):
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   145
            if mode=='red':
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   146
                html.append('<span class="diffchange-del">%s</span>'% (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   147
            else:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   148
                html.append('<del>%s</del>'% (text))                
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   149
        elif op == self.DIFF_EQUAL:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   150
          html.append("<SPAN>%s</SPAN>" % (text))
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   151
        if op != self.DIFF_DELETE:
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   152
          i += len(data)
cae2de810f77 improve diff between versions (word diff)
raph
parents: 149
diff changeset
   153
      return "".join(html)