This repository has been archived by the owner on Aug 26, 2022. It is now read-only.
/
helpers.py
97 lines (88 loc) · 3.69 KB
/
helpers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import difflib
import re
import constance.config
from jingo import register
import jinja2
from tidylib import tidy_document
from tower import ugettext as _
from wiki import DIFF_WRAP_COLUMN
from wiki import parser
# http://stackoverflow.com/q/774316/571420
def show_diff(seqm):
"""Unify operations between two compared strings
seqm is a difflib.SequenceMatcher instance whose a & b are strings"""
lines = constance.config.FEED_DIFF_CONTEXT_LINES
full_output= []
for opcode, a0, a1, b0, b1 in seqm.get_opcodes():
if opcode == 'equal':
full_output.append(seqm.a[a0:a1])
elif opcode == 'insert':
full_output.append("<ins>" + seqm.b[b0:b1] + "</ins>")
elif opcode == 'delete':
full_output.append("<del>" + seqm.a[a0:a1] + "</del>")
elif opcode == 'replace':
full_output.append(" <del>" + seqm.a[a0:a1] + "</del> ")
full_output.append(" <ins>" + seqm.b[b0:b1] + "</ins> ")
else:
raise RuntimeError, "unexpected opcode"
output = []
whitespace_change = False
for piece in full_output:
if '<ins>' in piece or '<del>' in piece:
# a change
if re.match('<(ins|del)>\W+</(ins|del)>', piece):
# the change is whitespace,
# ignore it and remove preceding context
output = output[:-lines]
whitespace_change = True
continue
else:
output.append(piece)
else:
context_lines = piece.splitlines()
if output == []:
# first context only shows preceding lines for next change
context = ['<p>...</p>'] + context_lines[-lines:]
elif whitespace_change:
# context shows preceding lines for next change
context = ['<p>...</p>'] + context_lines[-lines:]
whitespace_change = False
else:
# context shows subsequent lines
# and preceding lines for next change
context = context_lines[:lines] + ['<p>...</p>'] + context_lines[-lines:]
output = output + context
# remove extra context from the very end, unless its the only context
if len(output) > lines+1: # context lines and the change line
output = output[:-lines]
return ''.join(output)
def _massage_diff_content(content):
tidy_options = {'output-xhtml': 0, 'force-output': 1}
content = tidy_document(content, options=tidy_options)
return content
@register.function
def diff_table(content_from, content_to):
"""Creates an HTML diff of the passed in content_from and content_to."""
tidy_from, errors = _massage_diff_content(content_from)
tidy_to, errors = _massage_diff_content(content_to)
html_diff = difflib.HtmlDiff(wrapcolumn=DIFF_WRAP_COLUMN)
from_lines = tidy_from.splitlines()
to_lines = tidy_to.splitlines()
try:
diff = html_diff.make_table(from_lines, to_lines, context=True,
numlines=constance.config.DIFF_CONTEXT_LINES)
except RuntimeError:
# some diffs hit a max recursion error
message = _(u'There was an error generating the content.')
diff = '<div class="warning"><p>%s</p></div>' % message
return jinja2.Markup(diff)
@register.function
def diff_inline(content_from, content_to):
tidy_from, errors = _massage_diff_content(content_from)
tidy_to, errors = _massage_diff_content(content_to)
sm = difflib.SequenceMatcher(None, tidy_from, tidy_to)
diff = show_diff(sm)
return jinja2.Markup(diff)
@register.function
def generate_video(v):
return jinja2.Markup(parser.generate_video(v))