Librarian in regular requirements.
[redakcja.git] / apps / wiki / nice_diff.py
1 # -*- coding: utf-8 -*-
2 #
3 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 #
6 import difflib
7 import re
8 from collections import deque
9
10 from django.template.loader import render_to_string
11 from django.utils.html import escape as html_escape
12
13 DIFF_RE = re.compile(r"""\x00([+^-])""", re.UNICODE)
14 NAMES = {'+': 'added', '-': 'removed', '^': 'changed'}
15
16
17 def diff_replace(match):
18     return """<span class="diff_mark diff_mark_%s">""" % NAMES[match.group(1)]
19
20
21 def filter_line(line):
22     return  DIFF_RE.sub(diff_replace, html_escape(line)).replace('\x01', '</span>')
23
24
25 def format_changeset(a, b, change):
26     return (a[0], filter_line(a[1]), b[0], filter_line(b[1]), change)
27
28
29 def html_diff_table(la, lb, context=None):
30     all_changes = difflib._mdiff(la, lb)
31
32     if context is None:
33         changes = (format_changeset(*c) for c in all_changes)
34     else:
35         changes = []
36         q = deque()
37         after_change = False
38
39         for changeset in all_changes:
40             q.append(changeset)
41
42             if changeset[2]:
43                 after_change = True
44                 if not after_change:
45                     changes.append((0, '-----', 0, '-----', False))
46                 changes.extend(format_changeset(*c) for c in q)
47                 q.clear()
48             else:
49                 if len(q) == context and after_change:
50                     changes.extend(format_changeset(*c) for c in q)
51                     q.clear()
52                     after_change = False
53                 elif len(q) > context:
54                     q.popleft()
55
56     return render_to_string("wiki/diff_table.html", {
57         "changes": changes,
58     })
59
60
61 __all__ = ['html_diff_table']