diff --git a/issues/templates/issues/issue_stacktrace.html b/issues/templates/issues/issue_stacktrace.html
index dae1705..d00557f 100644
--- a/issues/templates/issues/issue_stacktrace.html
+++ b/issues/templates/issues/issue_stacktrace.html
@@ -26,6 +26,7 @@
{% for frame in exception.stacktrace.frames %}
+ {% with frame=frame|pygmentize %}
{# per frame div #}
@@ -47,8 +48,6 @@
{# convience div for padding & border; the border is basically the top-border of the next header #}
{# code listing #}
- {{ frame|pygmentize }}
- {% comment %}
{# the spread-out pX-6 in this code is intentional to ensure the padding is visible when scrolling to the right, and not visible when scrolling is possible (i.e. the text is cut-off awkwardly to hint at scrolling #}
{% for line in frame.pre_context %}{{ line }} {# leave space to avoid collapse #}
{% endfor %}
@@ -57,7 +56,6 @@
{{ frame.context_line }} {# leave space to avoid collapse #}
{% for line in frame.post_context %}{{ line }} {# leave space to avoid collapse #}
{% endfor %}
- {% endcomment %}
{% if frame.vars %}
@@ -80,6 +78,7 @@
{# per frame div #}
+ {% endwith %}
{% endfor %} {# frame #}
{#
#} {# per-exception div in the multi-exception case #}
diff --git a/theme/templatetags/issues.py b/theme/templatetags/issues.py
index 9ec6832..89af0eb 100644
--- a/theme/templatetags/issues.py
+++ b/theme/templatetags/issues.py
@@ -8,11 +8,42 @@ from django.utils.safestring import mark_safe
register = template.Library()
+def _split(joined, lengths):
+ result = []
+ start = 0
+ for length in lengths:
+ result.append(joined[start:start + length])
+ start += length
+
+ assert [len(r) for r in result] == lengths
+ return result
+
+
+def noempty(lines):
+ # if the line is empty, replace it with a space; this is needed to avoid pygments dropping empty lines (I've
+ # obvserved this for leading empty lines, but the problem might be more general)
+ # https://github.com/pygments/pygments/issues/2673
+ return [" " if not line else line for line in lines]
+
+
@register.filter
def pygmentize(value):
# first, get the actual code from the frame
- code = "\n".join(value['pre_context'] + [value['context_line']] + value['post_context'])
- return(mark_safe(highlight(code, PythonLexer(), HtmlFormatter())))
+ lengths = [len(value['pre_context']), 1, len(value['post_context'])]
+
+ code = "\n".join(noempty(value['pre_context']) + [value['context_line']] + noempty(value['post_context']))
+ pygments_result = highlight(code, PythonLexer(), HtmlFormatter(nowrap=True))
+ lines = pygments_result.split('\n')[:-1] # remove the last empty line, which is a result of split()
+
+ assert len(lines) == sum(lengths), "%d != %d" % (len(lines), sum(lengths))
+
+ pre_context, context_lines, post_context = _split(lines, lengths)
+
+ value['pre_context'] = [mark_safe(s) for s in pre_context]
+ value['context_line'] = mark_safe(context_lines[0])
+ value['post_context'] = [mark_safe(s) for s in post_context]
+
+ return value
@register.filter(name='firstlineno')