Skip to content

Commit

Permalink
Merge pull request #283 from isms/liquid-notebook-language
Browse files Browse the repository at this point in the history
[liquid_tags:notebook] Add language highlighting option
  • Loading branch information
justinmayer committed Aug 28, 2014
2 parents 13483ac + 5e01108 commit 50f426b
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 6 deletions.
31 changes: 28 additions & 3 deletions liquid_tags/Readme.md
Expand Up @@ -74,7 +74,7 @@ filename.
The script must be in the ``code`` subdirectory of your content folder:
this default location can be changed by specifying

CODE_DIR = 'code'
CODE_DIR = 'code'

within your configuration file. Additionally, in order for the resulting
hyperlink to work, this directory must be listed under the STATIC_PATHS
Expand All @@ -97,7 +97,7 @@ config file:
Because the conversion and rendering of notebooks is rather involved, there
are a few extra steps required for this plugin:

- First, you will need to install IPython >= 1.0 [1]_
- First, you will need to install IPython >= 1.0 [[1](#1)]

- After typing "make html" when using the notebook tag, a file called
``_nb_header.html`` will be produced in the main directory. The content
Expand All @@ -115,6 +115,31 @@ are a few extra steps required for this plugin:

this will insert the proper css formatting into your document.

### Optional Arguments for Notebook Tags

The notebook tag also has two optional arguments: ``cells`` and ``language``.

- You can specify a slice of cells to include:

``{% notebook filename.ipynb cells[2:8] %}``

- You can also specify the name of a language which Pygments should use for
highlighting code cells. A list of the short names for languages that Pygments
will highlight can be found [here](http://www.pygments.org/docs/lexers/).

``{% notebook filename.ipynb language[julia] %}``

This may be helpful for those using [IJulia](https://github.com/JuliaLang/IJulia.jl)
or notebooks in any other language, especially as the IPython project [broadens its
scope](https://github.com/ipython/ipython/wiki/Roadmap:-IPython) of [language
compatibility](http://jupyter.org/). By default, the language for highlighting
will be ``ipython``.

- These options can be used separately, together, or not at all. However,
if both tags are used then ``cells`` must come before ``language``:

``{% notebook filename.ipynb cells[2:8] language[julia] %}``

### Collapsible Code in IPython Notebooks

The plugin also enables collapsible code input boxes. For this to work
Expand All @@ -127,4 +152,4 @@ comment line ``# <!-- collapse=False -->`` will be open on load but
can be collapsed by clicking on their header. Cells without collapse
comments are rendered as standard code input cells.

[1] http://ipython.org/
[<a name="1">1</a>] http://ipython.org/
11 changes: 8 additions & 3 deletions liquid_tags/notebook.py
Expand Up @@ -46,6 +46,8 @@
"""
import re
import os
from functools import partial

from .mdx_liquid_tags import LiquidTags

from distutils.version import LooseVersion
Expand Down Expand Up @@ -230,8 +232,8 @@ def custom_highlighter(source, language='ipython', metadata=None):
#----------------------------------------------------------------------
# Below is the pelican plugin code.
#
SYNTAX = "{% notebook /path/to/notebook.ipynb [ cells[start:end] ] %}"
FORMAT = re.compile(r"""^(\s+)?(?P<src>\S+)(\s+)?((cells\[)(?P<start>-?[0-9]*):(?P<end>-?[0-9]*)(\]))?(\s+)?$""")
SYNTAX = "{% notebook /path/to/notebook.ipynb [ cells[start:end] ] [ language[language] ] %}"
FORMAT = re.compile(r"""^(\s+)?(?P<src>\S+)(\s+)?((cells\[)(?P<start>-?[0-9]*):(?P<end>-?[0-9]*)(\]))?(\s+)?((language\[)(?P<language>-?[a-z0-9\+\-]*)(\]))?(\s+)?$""")


@LiquidTags.register('notebook')
Expand All @@ -242,6 +244,7 @@ def notebook(preprocessor, tag, markup):
src = argdict['src']
start = argdict['start']
end = argdict['end']
language = argdict['language']
else:
raise ValueError("Error processing input, "
"expected syntax: {0}".format(SYNTAX))
Expand All @@ -256,6 +259,8 @@ def notebook(preprocessor, tag, markup):
else:
end = None

language_applied_highlighter = partial(custom_highlighter, language=language)

settings = preprocessor.configs.config['settings']
nb_dir = settings.get('NOTEBOOK_DIR', 'notebooks')
nb_path = os.path.join('content', nb_dir, src)
Expand Down Expand Up @@ -284,7 +289,7 @@ def notebook(preprocessor, tag, markup):

exporter = HTMLExporter(config=c,
template_file=template_file,
filters={'highlight2html': custom_highlighter},
filters={'highlight2html': language_applied_highlighter},
**subcell_kwarg)

# read and parse the notebook
Expand Down
92 changes: 92 additions & 0 deletions liquid_tags/test_notebook.py
@@ -0,0 +1,92 @@
import re

from pelican.tests.support import unittest

import notebook


class TestNotebookTagRegex(unittest.TestCase):

def get_argdict(self, markup):

match = notebook.FORMAT.search(markup)

if match:
argdict = match.groupdict()

src = argdict['src']
start = argdict['start']
end = argdict['end']
language = argdict['language']

return src, start, end, language

return None

def test_basic_notebook_tag(self):
markup = u'path/to/thing.ipynb'
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertIsNone(start)
self.assertIsNone(end)
self.assertIsNone(language)

def test_basic_notebook_tag_insensitive_to_whitespace(self):
markup = u' path/to/thing.ipynb '
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertIsNone(start)
self.assertIsNone(end)
self.assertIsNone(language)

def test_notebook_tag_with_cells(self):
markup = u'path/to/thing.ipynb cells[1:5]'
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertEqual(start, u'1')
self.assertEqual(end, u'5')
self.assertIsNone(language)

def test_notebook_tag_with_alphanumeric_language(self):
markup = u'path/to/thing.ipynb language[python3]'
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertIsNone(start)
self.assertIsNone(end)
self.assertEqual(language, u'python3')

def test_notebook_tag_with_symbol_in_name_language(self):
for short_name in [u'c++', u'cpp-objdump', u'c++-objdumb', u'cxx-objdump']:
markup = u'path/to/thing.ipynb language[{}]'.format(short_name)
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertIsNone(start)
self.assertIsNone(end)
self.assertEqual(language, short_name)

def test_notebook_tag_with_language_and_cells(self):
markup = u'path/to/thing.ipynb cells[1:5] language[julia]'
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertEqual(start, u'1')
self.assertEqual(end, u'5')
self.assertEqual(language, u'julia')

def test_notebook_tag_with_language_and_cells_and_weird_spaces(self):
markup = u' path/to/thing.ipynb cells[1:5] language[julia] '
src, start, end, language = self.get_argdict(markup)

self.assertEqual(src, u'path/to/thing.ipynb')
self.assertEqual(start, u'1')
self.assertEqual(end, u'5')
self.assertEqual(language, u'julia')


if __name__ == '__main__':
unittest.main()

0 comments on commit 50f426b

Please sign in to comment.