From 009a294cba472233585a4c1dbbbba6e3cfc2cb59 2013-09-18 17:52:45 From: Pablo de Oliveira Date: 2013-09-18 17:52:45 Subject: [PATCH] Select adequate highlighter for cell magic languages Detect cells that use language extension magics such as %%R or %%bash and call the appropriate pygmentize lexer. Closes #4184 --- diff --git a/IPython/nbconvert/filters/highlight.py b/IPython/nbconvert/filters/highlight.py index afb05b5..bda258d 100644 --- a/IPython/nbconvert/filters/highlight.py +++ b/IPython/nbconvert/filters/highlight.py @@ -14,7 +14,9 @@ from within Jinja templates. # Imports #----------------------------------------------------------------------------- -from pygments import highlight as pygements_highlight +import re + +from pygments import highlight as pygements_highlight from pygments.lexers import get_lexer_by_name from pygments.formatters import HtmlFormatter from pygments.formatters import LatexFormatter @@ -28,6 +30,20 @@ from IPython.nbconvert.utils.lexers import IPythonLexer MULTILINE_OUTPUTS = ['text', 'html', 'svg', 'latex', 'javascript', 'json'] +# list of magic language extensions and their associated pygment lexers +magic_languages = {'%%R': 'r', + '%%bash': 'bash', + '%%octave': 'octave', + '%%perl': 'perl', + '%%ruby': 'ruby' + } + +# build a RE to catch language extensions and choose an adequate +# pygments lexer +re_languages = "|".join(magic_languages.keys()) +re_magic_language = re.compile(r'^\s*({})\s+'.format(re_languages), + re.MULTILINE) + #----------------------------------------------------------------------------- # Utility functions #----------------------------------------------------------------------------- @@ -41,7 +57,7 @@ __all__ = [ def highlight2html(source, language='ipython'): """ Return a syntax-highlighted version of the input source as html output. - + Parameters ---------- source : str @@ -49,14 +65,14 @@ def highlight2html(source, language='ipython'): language : str Language to highlight the syntax of. """ - + return _pygment_highlight(source, HtmlFormatter(), language) def highlight2latex(source, language='ipython'): """ Return a syntax-highlighted version of the input source as latex output. - + Parameters ---------- source : str @@ -67,10 +83,33 @@ def highlight2latex(source, language='ipython'): return _pygment_highlight(source, LatexFormatter(), language) +def which_magic_language(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 = re_magic_language.search(source) + + if m: + # By construction of the re, the matched language must be in the + # language dictionnary + assert(m.group(1) in magic_languages) + return magic_languages[m.group(1)] + else: + return None + + def _pygment_highlight(source, output_formatter, language='ipython'): """ Return a syntax-highlighted version of the input source - + Parameters ---------- source : str @@ -79,10 +118,14 @@ def _pygment_highlight(source, output_formatter, language='ipython'): language : str Language to highlight the syntax of. """ - + + magic_language = which_magic_language(source) + if magic_language: + language = magic_language + if language == 'ipython': lexer = IPythonLexer() else: lexer = get_lexer_by_name(language, stripall=True) - return pygements_highlight(source, lexer, output_formatter) + return pygements_highlight(source, lexer, output_formatter) diff --git a/IPython/nbconvert/filters/tests/test_highlight.py b/IPython/nbconvert/filters/tests/test_highlight.py index df25a42..faa5791 100644 --- a/IPython/nbconvert/filters/tests/test_highlight.py +++ b/IPython/nbconvert/filters/tests/test_highlight.py @@ -15,7 +15,7 @@ Module with tests for Highlight #----------------------------------------------------------------------------- from ...tests.base import TestsBase -from ..highlight import highlight2html, highlight2latex +from ..highlight import highlight2html, highlight2latex, which_magic_language #----------------------------------------------------------------------------- @@ -39,27 +39,59 @@ class TestHighlight(TestsBase): %%pylab plot(x,y, 'r') """ - ] + ] tokens = [ ['Hello World Example', 'say', 'text', 'print', 'def'], ['pylab', 'plot']] - def test_highlight2html(self): """highlight2html test""" for index, test in enumerate(self.tests): self._try_highlight(highlight2html, test, self.tokens[index]) - def test_highlight2latex(self): """highlight2latex test""" for index, test in enumerate(self.tests): self._try_highlight(highlight2latex, test, self.tokens[index]) - def _try_highlight(self, method, test, tokens): """Try highlighting source, look for key tokens""" results = method(test) for token in tokens: assert token in results + + magic_tests = { + 'r': + """%%R -i x,y -o XYcoef + lm.fit <- lm(y~x) + par(mfrow=c(2,2)) + print(summary(lm.fit)) + plot(lm.fit) + XYcoef <- coef(lm.fit) + """, + 'bash': + """# the following code is in bash + %%bash + echo "test" > out + """, + 'ipython': + """# this should not be detected + print("%%R") + """ + } + + def test_highlight_rmagic(self): + """Test %%R magic highlighting""" + language = which_magic_language(self.magic_tests['r']) + assert language == 'r' + + def test_highlight_bashmagic(self): + """Test %%bash magic highlighting""" + language = which_magic_language(self.magic_tests['bash']) + assert language == 'bash' + + def test_highlight_interferemagic(self): + """Test that magic highlighting does not interfere with ipython code""" + language = which_magic_language(self.magic_tests['ipython']) + assert language == None \ No newline at end of file