1# -*- coding: utf-8 -*-
2r"""
3Template filter for rendering a string with syntax highlighting.
4It relies on Pygments to accomplish this.
5
6Some standard usage examples (from within Django templates).
7Coloring a string with the Python lexer:
8
9    {% load syntax_color %}
10    {{ code_string|colorize:"python" }}
11
12You may use any lexer in Pygments. The complete list of which
13can be found [on the Pygments website][1].
14
15[1]: http://pygments.org/docs/lexers/
16
17You may also have Pygments attempt to guess the correct lexer for
18a particular string. However, if may not be able to choose a lexer,
19in which case it will simply return the string unmodified. This is
20less efficient compared to specifying the lexer to use.
21
22    {{ code_string|colorize }}
23
24You may also render the syntax highlighed text with line numbers.
25
26    {% load syntax_color %}
27    {{ some_code|colorize_table:"html+django" }}
28    {{ let_pygments_pick_for_this_code|colorize_table }}
29
30Please note that before you can load the ``syntax_color`` template filters
31you will need to add the ``django_extensions.utils`` application to the
32``INSTALLED_APPS``setting in your project's ``settings.py`` file.
33"""
34import os
35
36from django import template
37from django.template.defaultfilters import stringfilter
38from django.utils.safestring import mark_safe
39
40try:
41    from pygments import highlight
42    from pygments.formatters import HtmlFormatter
43    from pygments.lexers import get_lexer_by_name, guess_lexer, ClassNotFound
44    HAS_PYGMENTS = True
45except ImportError:  # pragma: no cover
46    HAS_PYGMENTS = False
47
48__author__ = 'Will Larson <lethain@gmail.com>'
49
50
51register = template.Library()
52
53
54def pygments_required(func):
55    """Raise ImportError if pygments is not installed."""
56    def wrapper(*args, **kwargs):
57        if not HAS_PYGMENTS:  # pragma: no cover
58            raise ImportError(
59                "Please install 'pygments' library to use syntax_color.")
60        rv = func(*args, **kwargs)
61        return rv
62    return wrapper
63
64
65@pygments_required
66@register.simple_tag
67def pygments_css():
68    return HtmlFormatter().get_style_defs('.highlight')
69
70
71def generate_pygments_css(path=None):
72    path = os.path.join(path or os.getcwd(), 'pygments.css')
73    f = open(path, 'w')
74    f.write(pygments_css())
75    f.close()
76
77
78def get_lexer(value, arg):
79    if arg is None:
80        return guess_lexer(value)
81    return get_lexer_by_name(arg)
82
83
84@pygments_required
85@register.filter(name='colorize')
86@stringfilter
87def colorize(value, arg=None):
88    try:
89        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter()))
90    except ClassNotFound:
91        return value
92
93
94@pygments_required
95@register.filter(name='colorize_table')
96@stringfilter
97def colorize_table(value, arg=None):
98    try:
99        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(linenos='table')))
100    except ClassNotFound:
101        return value
102
103
104@pygments_required
105@register.filter(name='colorize_noclasses')
106@stringfilter
107def colorize_noclasses(value, arg=None):
108    try:
109        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(noclasses=True)))
110    except ClassNotFound:
111        return value
112