File size: 3,197 Bytes
d1ceb73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
98
99
100
101
102
"""This preprocessor detect cells using a different language through
magic extensions such as `%%R` or `%%octave`. Cell's metadata is marked
so that the appropriate highlighter can be used in the `highlight`
filter.
"""

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

import re

from traitlets import Dict

from .base import Preprocessor


class HighlightMagicsPreprocessor(Preprocessor):
    """
    Detects and tags code cells that use a different languages than Python.
    """

    # list of magic language extensions and their associated pygment lexers
    default_languages = Dict(
        {
            "%%R": "r",
            "%%bash": "bash",
            "%%cython": "cython",
            "%%javascript": "javascript",
            "%%julia": "julia",
            "%%latex": "latex",
            "%%octave": "octave",
            "%%perl": "perl",
            "%%ruby": "ruby",
            "%%sh": "sh",
            "%%sql": "sql",
        }
    )

    # user defined language extensions
    languages = Dict(
        help=(
            "Syntax highlighting for magic's extension languages. "
            "Each item associates a language magic extension such as %%R, "
            "with a pygments lexer such as r."
        )
    ).tag(config=True)

    def __init__(self, config=None, **kw):
        """Public constructor"""

        super().__init__(config=config, **kw)

        # Update the default languages dict with the user configured ones
        self.default_languages.update(self.languages)

        # build a regular expression to catch language extensions and choose
        # an adequate pygments lexer
        any_language = "|".join(self.default_languages.keys())
        self.re_magic_language = re.compile(rf"^\s*({any_language})\s+")

    def which_magic_language(self, source):
        """
        When a cell uses another language through a magic extension,
        the other language is returned.
        If no language magic is detected, this function returns None.

        Parameters
        ----------
        source: str
            Source code of the cell to highlight
        """

        m = self.re_magic_language.match(source)

        if m:
            # By construction of the re, the matched language must be in the
            # languages dictionary
            return self.default_languages[m.group(1)]
        return None

    def preprocess_cell(self, cell, resources, cell_index):
        """
        Tags cells using a magic extension language

        Parameters
        ----------
        cell : NotebookNode cell
            Notebook cell being processed
        resources : dictionary
            Additional resources used in the conversion process.  Allows
            preprocessors to pass variables into the Jinja engine.
        cell_index : int
            Index of the cell being processed (see base.py)
        """

        # Only tag code cells
        if cell.cell_type == "code":
            magic_language = self.which_magic_language(cell.source)
            if magic_language:
                cell["metadata"]["magics_language"] = magic_language
        return cell, resources