浏览代码

Add language highlighting option to notebook tag

Update documentation and fix small errors
Add tests for notebook tag regex

Update SYNTAX string to match new regex

Explicitly outline necessary order for tag options

Addresses concern in
https://github.com/getpelican/pelican-plugins/pull/283#issuecomment-53731077
isms 10 年之前
父节点
当前提交
5e01108141
共有 3 个文件被更改,包括 128 次插入6 次删除
  1. 28 3
      liquid_tags/Readme.md
  2. 8 3
      liquid_tags/notebook.py
  3. 92 0
      liquid_tags/test_notebook.py

+ 28 - 3
liquid_tags/Readme.md

@@ -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
@@ -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
@@ -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
@@ -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/

+ 8 - 3
liquid_tags/notebook.py

@@ -46,6 +46,8 @@ still be some conflicts.
 """
 import re
 import os
+from functools import partial
+
 from .mdx_liquid_tags import LiquidTags
 
 from distutils.version import LooseVersion
@@ -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')
@@ -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))
@@ -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)
@@ -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

+ 92 - 0
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()