Repository: Python-Markdown/markdown
Pull request #686 (2018-07-24 18:44:31)

Add toc_depth parameter to toc extension
This is a clone of #431 rebased with conflicts resolved.


Patch:
diff --git a/docs/extensions/toc.md b/docs/extensions/toc.md
index e358132..5038428 100644
--- a/docs/extensions/toc.md
+++ b/docs/extensions/toc.md
@@ -134,3 +134,11 @@ The following options are provided to configure the output:
 
 * **`separator`**:
     Word separator. Character which replaces white space in id. Defaults to "`-`".
+
+* **`toc_depth`**
+    Define up to which section level "n" (`<h1>` to `<hn>`, where `1 <= n <= 6`)
+    to include in the Table of Contents. Defaults to `6`.
+
+    When used with conjunction with `baselevel` this parameter will limit the
+    resulting (adjusted) heading. That is, if both `toc_depth` and `baselevel`
+    are 3, then only the highest level will be present in the table.
diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py
index 0e9b2b9..5783f3d 100644
--- a/markdown/extensions/toc.py
+++ b/markdown/extensions/toc.py
@@ -134,8 +134,8 @@ class TocTreeprocessor(Treeprocessor):
         self.use_permalinks = parseBoolValue(config["permalink"], False)
         if self.use_permalinks is None:
             self.use_permalinks = config["permalink"]
-
         self.header_rgx = re.compile("[Hh][123456]")
+        self.toc_depth = config["toc_depth"]
 
     def iterparent(self, node):
         ''' Iterator wrapper to get allowed parent and child all at once. '''
@@ -234,6 +234,8 @@ class TocTreeprocessor(Treeprocessor):
         for el in doc.iter():
             if isinstance(el.tag, string_type) and self.header_rgx.match(el.tag):
                 self.set_level(el)
+                if int(el.tag[-1]) > int(self.toc_depth):
+                    continue
                 text = ''.join(el.itertext()).strip()
 
                 # Do not override pre-existing ids
@@ -285,7 +287,10 @@ class TocExtension(Extension):
             "slugify": [slugify,
                         "Function to generate anchors based on header text - "
                         "Defaults to the headerid ext's slugify function."],
-            'separator': ['-', 'Word separator. Defaults to "-".']
+            'separator': ['-', 'Word separator. Defaults to "-".'],
+            "toc_depth": [6,
+                          "Define up to which section level n (<h1>..<hn>) to "
+                          "include in the TOC"]
         }
 
         super(TocExtension, self).__init__(**kwargs)
diff --git a/markdown/serializers.py b/markdown/serializers.py
index 187d755..308cf7a 100644
--- a/markdown/serializers.py
+++ b/markdown/serializers.py
@@ -51,7 +51,7 @@ else:  # pragma: no cover
 __all__ = ['to_html_string', 'to_xhtml_string']
 
 HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
-              "img", "input", "isindex", "link", "meta" "param")
+              "img", "input", "isindex", "link", "meta", "param")
 
 try:
     HTML_EMPTY = set(HTML_EMPTY)


Test patch:
diff --git a/tests/test_extensions.py b/tests/test_extensions.py
index 35eaf1d..7e2dad1 100644
--- a/tests/test_extensions.py
+++ b/tests/test_extensions.py
@@ -894,6 +894,60 @@ class TestTOC(TestCaseWithAssertStartsWith):
             '<h1 id="toc"><em>[TOC]</em></h1>'          # noqa
         )
 
+    def testMaxLevel(self):
+        """ Test toc_depth setting """
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.toc.TocExtension(toc_depth=2)]
+        )
+        text = '# Header 1\n\n## Header 2\n\n###Header 3 not in TOC'
+        self.assertEqual(
+            md.convert(text),
+            '<h1 id="header-1">Header 1</h1>\n'
+            '<h2 id="header-2">Header 2</h2>\n'
+            '<h3>Header 3 not in TOC</h3>'
+        )
+        self.assertEqual(
+            md.toc,
+            '<div class="toc">\n'
+              '<ul>\n'                                             # noqa
+                '<li><a href="#header-1">Header 1</a>'             # noqa
+                  '<ul>\n'                                         # noqa
+                    '<li><a href="#header-2">Header 2</a></li>\n'  # noqa
+                  '</ul>\n'                                        # noqa
+                '</li>\n'                                          # noqa
+              '</ul>\n'                                            # noqa
+            '</div>\n'
+        )
+
+        self.assertNotIn("Header 3", md.toc)
+
+    def testMaxLevelwithBaseLevel(self):
+        """ Test toc_depth setting together with baselevel """
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.toc.TocExtension(toc_depth=3,
+                                                             baselevel=2)]
+        )
+        text = '# Some Header\n\n## Next Level\n\n### Too High'
+        self.assertEqual(
+            md.convert(text),
+            '<h2 id="some-header">Some Header</h2>\n'
+            '<h3 id="next-level">Next Level</h3>\n'
+            '<h4>Too High</h4>'
+        )
+        self.assertEqual(
+            md.toc,
+            '<div class="toc">\n'
+              '<ul>\n'                                                 # noqa
+                '<li><a href="#some-header">Some Header</a>'           # noqa
+                  '<ul>\n'                                             # noqa
+                    '<li><a href="#next-level">Next Level</a></li>\n'  # noqa
+                  '</ul>\n'                                            # noqa
+                '</li>\n'                                              # noqa
+              '</ul>\n'                                                # noqa
+            '</div>\n'
+        )
+        self.assertNotIn("Too High", md.toc)
+
 
 class TestSmarty(unittest.TestCase):
     def setUp(self):