1"""This preprocessor detect cells using a different language through
2magic extensions such as `%%R` or `%%octave`. Cell's metadata is marked
3so that the appropriate highlighter can be used in the `highlight`
4filter.
5"""
6
7# Copyright (c) Jupyter Development Team.
8# Distributed under the terms of the Modified BSD License.
9
10import re
11
12from traitlets import Dict
13
14from .base import Preprocessor
15
16
17class HighlightMagicsPreprocessor(Preprocessor):
18 """
19 Detects and tags code cells that use a different languages than Python.
20 """
21
22 # list of magic language extensions and their associated pygment lexers
23 default_languages = Dict(
24 {
25 "%%R": "r",
26 "%%bash": "bash",
27 "%%cython": "cython",
28 "%%javascript": "javascript",
29 "%%julia": "julia",
30 "%%latex": "latex",
31 "%%octave": "octave",
32 "%%perl": "perl",
33 "%%ruby": "ruby",
34 "%%sh": "sh",
35 "%%sql": "sql",
36 }
37 )
38
39 # user defined language extensions
40 languages = Dict(
41 help=(
42 "Syntax highlighting for magic's extension languages. "
43 "Each item associates a language magic extension such as %%R, "
44 "with a pygments lexer such as r."
45 )
46 ).tag(config=True)
47
48 def __init__(self, config=None, **kw):
49 """Public constructor"""
50
51 super().__init__(config=config, **kw)
52
53 # Update the default languages dict with the user configured ones
54 self.default_languages.update(self.languages)
55
56 # build a regular expression to catch language extensions and choose
57 # an adequate pygments lexer
58 any_language = "|".join(self.default_languages.keys())
59 self.re_magic_language = re.compile(rf"^\s*({any_language})\s+")
60
61 def which_magic_language(self, source):
62 """
63 When a cell uses another language through a magic extension,
64 the other language is returned.
65 If no language magic is detected, this function returns None.
66
67 Parameters
68 ----------
69 source: str
70 Source code of the cell to highlight
71 """
72
73 m = self.re_magic_language.match(source)
74
75 if m:
76 # By construction of the re, the matched language must be in the
77 # languages dictionary
78 return self.default_languages[m.group(1)]
79 return None
80
81 def preprocess_cell(self, cell, resources, cell_index):
82 """
83 Tags cells using a magic extension language
84
85 Parameters
86 ----------
87 cell : NotebookNode cell
88 Notebook cell being processed
89 resources : dictionary
90 Additional resources used in the conversion process. Allows
91 preprocessors to pass variables into the Jinja engine.
92 cell_index : int
93 Index of the cell being processed (see base.py)
94 """
95
96 # Only tag code cells
97 if cell.cell_type == "code":
98 magic_language = self.which_magic_language(cell.source)
99 if magic_language:
100 cell["metadata"]["magics_language"] = magic_language
101 return cell, resources