1"""
2Module containing filter functions that allow code to be highlighted
3from within Jinja templates.
4"""
5
6# Copyright (c) IPython Development Team.
7# Distributed under the terms of the Modified BSD License.
8
9# pygments must not be imported at the module level
10# because errors should be raised at runtime if it's actually needed,
11# not import time, when it may not be needed.
12
13from html import escape
14from warnings import warn
15
16from traitlets import Dict, observe
17
18from nbconvert.utils.base import NbConvertBase
19
20MULTILINE_OUTPUTS = ["text", "html", "svg", "latex", "javascript", "json"]
21
22__all__ = ["Highlight2HTML", "Highlight2Latex"]
23
24
25class Highlight2HTML(NbConvertBase):
26 """Convert highlighted code to html."""
27
28 extra_formatter_options = Dict(
29 {},
30 help="""
31 Extra set of options to control how code is highlighted.
32
33 Passed through to the pygments' HtmlFormatter class.
34 See available list in https://pygments.org/docs/formatters/#HtmlFormatter
35 """,
36 config=True,
37 )
38
39 def __init__(self, pygments_lexer=None, **kwargs):
40 """Initialize the converter."""
41 self.pygments_lexer = pygments_lexer or "ipython3"
42 super().__init__(**kwargs)
43
44 @observe("default_language")
45 def _default_language_changed(self, change):
46 warn(
47 "Setting default_language in config is deprecated as of 5.0, "
48 "please use language_info metadata instead.",
49 stacklevel=2,
50 )
51 self.pygments_lexer = change["new"]
52
53 def __call__(self, source, language=None, metadata=None):
54 """
55 Return a syntax-highlighted version of the input source as html output.
56
57 Parameters
58 ----------
59 source : str
60 source of the cell to highlight
61 language : str
62 language to highlight the syntax of
63 metadata : NotebookNode cell metadata
64 metadata of the cell to highlight
65 """
66 from pygments.formatters import HtmlFormatter
67
68 if not language:
69 language = self.pygments_lexer
70
71 return _pygments_highlight(
72 source if len(source) > 0 else " ",
73 # needed to help post processors:
74 HtmlFormatter(
75 cssclass=escape(f" highlight hl-{language}"), **self.extra_formatter_options
76 ),
77 language,
78 metadata,
79 )
80
81
82class Highlight2Latex(NbConvertBase):
83 """Convert highlighted code to latex."""
84
85 extra_formatter_options = Dict(
86 {},
87 help="""
88 Extra set of options to control how code is highlighted.
89
90 Passed through to the pygments' LatexFormatter class.
91 See available list in https://pygments.org/docs/formatters/#LatexFormatter
92 """,
93 config=True,
94 )
95
96 def __init__(self, pygments_lexer=None, **kwargs):
97 """Initialize the converter."""
98 self.pygments_lexer = pygments_lexer or "ipython3"
99 super().__init__(**kwargs)
100
101 @observe("default_language")
102 def _default_language_changed(self, change):
103 warn(
104 "Setting default_language in config is deprecated as of 5.0, "
105 "please use language_info metadata instead.",
106 stacklevel=2,
107 )
108 self.pygments_lexer = change["new"]
109
110 def __call__(self, source, language=None, metadata=None, strip_verbatim=False):
111 """
112 Return a syntax-highlighted version of the input source as latex output.
113
114 Parameters
115 ----------
116 source : str
117 source of the cell to highlight
118 language : str
119 language to highlight the syntax of
120 metadata : NotebookNode cell metadata
121 metadata of the cell to highlight
122 strip_verbatim : bool
123 remove the Verbatim environment that pygments provides by default
124 """
125 from pygments.formatters import LatexFormatter
126
127 if not language:
128 language = self.pygments_lexer
129
130 latex = _pygments_highlight(
131 source, LatexFormatter(**self.extra_formatter_options), language, metadata
132 )
133 if strip_verbatim:
134 latex = latex.replace(r"\begin{Verbatim}[commandchars=\\\{\}]" + "\n", "")
135 return latex.replace("\n\\end{Verbatim}\n", "")
136 return latex
137
138
139def _pygments_highlight(
140 source, output_formatter, language="ipython", metadata=None, **lexer_options
141):
142 """
143 Return a syntax-highlighted version of the input source
144
145 Parameters
146 ----------
147 source : str
148 source of the cell to highlight
149 output_formatter : Pygments formatter
150 language : str
151 language to highlight the syntax of
152 metadata : NotebookNode cell metadata
153 metadata of the cell to highlight
154 lexer_options : dict
155 Options to pass to the pygments lexer. See
156 https://pygments.org/docs/lexers/#available-lexers for more information about
157 valid lexer options
158 """
159 from pygments import highlight
160 from pygments.lexers import get_lexer_by_name
161 from pygments.util import ClassNotFound
162
163 # If the cell uses a magic extension language,
164 # use the magic language instead.
165 if language.startswith("ipython") and metadata and "magics_language" in metadata:
166 language = metadata["magics_language"]
167
168 lexer = None
169 if language == "ipython2":
170 try:
171 from IPython.lib.lexers import IPythonLexer
172 except ImportError:
173 warn("IPython lexer unavailable, falling back on Python", stacklevel=2)
174 language = "python"
175 else:
176 lexer = IPythonLexer()
177 elif language == "ipython3":
178 try:
179 from IPython.lib.lexers import IPython3Lexer
180 except ImportError:
181 warn("IPython3 lexer unavailable, falling back on Python 3", stacklevel=2)
182 language = "python3"
183 else:
184 lexer = IPython3Lexer()
185
186 if lexer is None:
187 try:
188 lexer = get_lexer_by_name(language, **lexer_options)
189 except ClassNotFound:
190 warn("No lexer found for language %r. Treating as plain text." % language, stacklevel=2)
191 from pygments.lexers.special import TextLexer
192
193 lexer = TextLexer()
194
195 return highlight(source, lexer, output_formatter)