From b434317118105c80f1451fa0545776d93fb4dc38 Mon Sep 17 00:00:00 2001 From: rundef Date: Sun, 9 Nov 2025 14:45:00 -0500 Subject: [PATCH 1/4] toc: add option to limit which heading levels get anchor links --- docs/extensions/toc.md | 18 ++++++------------ markdown/extensions/toc.py | 7 ++++++- tests/test_syntax/extensions/test_toc.py | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/docs/extensions/toc.md b/docs/extensions/toc.md index 6db62226f..3ab8c8763 100644 --- a/docs/extensions/toc.md +++ b/docs/extensions/toc.md @@ -239,18 +239,12 @@ The following options are provided to configure the output: Word separator. Character which replaces white space in id. Defaults to "`-`". * **`toc_depth`** - Define the range of section levels to include in the Table of Contents. - A single integer (`b`) defines the bottom section level (`

..`) only. - A string consisting of two digits separated by a hyphen in between (`"2-5"`), - define the top (`t`) and the bottom (`b`) (`..`). Defaults to `6` (bottom). - - When used with conjunction with `baselevel`, this parameter will not - take the fitted hierarchy from `baselevel` into account. That is, if - both `toc_depth` and `baselevel` are `3`, then only the highest level - will be present in the table. If you set `baselevel` to `3` and - `toc_depth` to `"2-6"`, the *first* headline will be `

` and so still - included in the Table of Contents. To exclude this first level, you - have to set `toc_depth` to `"4-6"`. + Define the highest heading level (deepest ``) for which to add anchor links or permalinks. + Defaults to `6` + + For example, setting max_level to `2` adds anchors only to `

` and `

` elements. + +* **`max_level`** A trivial example: diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index c92f928b6..13212e392 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -257,7 +257,7 @@ def __init__(self, md: Markdown, config: dict[str, Any]): self.permalink_class: str = config["permalink_class"] self.permalink_title: str = config["permalink_title"] self.permalink_leading: bool | None = parseBoolValue(config["permalink_leading"], False) - self.header_rgx = re.compile("[Hh][123456]") + self.header_rgx = re.compile(f'[Hh][1-{config["max_level"]}') if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]: self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')] else: @@ -466,6 +466,11 @@ def __init__(self, **kwargs): 'separated by a hyphen in between (`2-5`) defines the top (t) and the bottom (b) (..). ' 'Default: `6` (bottom).' ], + 'max_level': [ + 6, + 'Define the max heading level for which to add anchors/permalinks.' + 'Default: `6`' + ] } """ Default configuration options. """ diff --git a/tests/test_syntax/extensions/test_toc.py b/tests/test_syntax/extensions/test_toc.py index 372ba09cc..54c1ed63a 100644 --- a/tests/test_syntax/extensions/test_toc.py +++ b/tests/test_syntax/extensions/test_toc.py @@ -555,6 +555,24 @@ def testAnchorLinkWithDoubleInlineCode(self): extensions=[TocExtension(anchorlink=True)] ) + def testAnchorLinkWithMaxLevel(self): + self.assertMarkdownRenders( + self.dedent( + ''' + # Header 1 + + ## Header *2* + ''' + ), + self.dedent( + ''' +

Header 1

+

Header 2

+ ''' + ), + extensions=[TocExtension(anchorlink=True, max_level=1)] + ) + def testPermalink(self): self.assertMarkdownRenders( '# Header', From c0168447fcfae45f5a75701059b920d5ce4bc067 Mon Sep 17 00:00:00 2001 From: rundef Date: Sun, 9 Nov 2025 14:46:57 -0500 Subject: [PATCH 2/4] Typos --- docs/extensions/toc.md | 18 +++++++++++++++--- markdown/extensions/toc.py | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/extensions/toc.md b/docs/extensions/toc.md index 3ab8c8763..8e295a51b 100644 --- a/docs/extensions/toc.md +++ b/docs/extensions/toc.md @@ -239,13 +239,25 @@ The following options are provided to configure the output: Word separator. Character which replaces white space in id. Defaults to "`-`". * **`toc_depth`** + Define the range of section levels to include in the Table of Contents. + A single integer (`b`) defines the bottom section level (`

..`) only. + A string consisting of two digits separated by a hyphen in between (`"2-5"`), + define the top (`t`) and the bottom (`b`) (`..`). Defaults to `6` (bottom). + + When used with conjunction with `baselevel`, this parameter will not + take the fitted hierarchy from `baselevel` into account. That is, if + both `toc_depth` and `baselevel` are `3`, then only the highest level + will be present in the table. If you set `baselevel` to `3` and + `toc_depth` to `"2-6"`, the *first* headline will be `

` and so still + included in the Table of Contents. To exclude this first level, you + have to set `toc_depth` to `"4-6"`. + +* **`max_level`** Define the highest heading level (deepest ``) for which to add anchor links or permalinks. - Defaults to `6` + Defaults to `6`. For example, setting max_level to `2` adds anchors only to `

` and `

` elements. -* **`max_level`** - A trivial example: ```python diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index 13212e392..22d1c75af 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -257,7 +257,7 @@ def __init__(self, md: Markdown, config: dict[str, Any]): self.permalink_class: str = config["permalink_class"] self.permalink_title: str = config["permalink_title"] self.permalink_leading: bool | None = parseBoolValue(config["permalink_leading"], False) - self.header_rgx = re.compile(f'[Hh][1-{config["max_level"]}') + self.header_rgx = re.compile(f'[Hh][1-{config["max_level"]}]') if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]: self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')] else: From b0bd292ea96cff60d321dc1f8e1804be4691dc4e Mon Sep 17 00:00:00 2001 From: rundef Date: Sun, 9 Nov 2025 14:50:31 -0500 Subject: [PATCH 3/4] Fix failing test --- tests/test_syntax/extensions/test_toc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_syntax/extensions/test_toc.py b/tests/test_syntax/extensions/test_toc.py index 54c1ed63a..6259e7860 100644 --- a/tests/test_syntax/extensions/test_toc.py +++ b/tests/test_syntax/extensions/test_toc.py @@ -567,7 +567,7 @@ def testAnchorLinkWithMaxLevel(self): self.dedent( '''

Header 1

-

Header 2

+

Header 2

''' ), extensions=[TocExtension(anchorlink=True, max_level=1)] From 367270c18be4269f66b8fadf702f5fdfc4098a5d Mon Sep 17 00:00:00 2001 From: rundef Date: Wed, 12 Nov 2025 18:36:14 -0500 Subject: [PATCH 4/4] Rename from max_level to link_depth --- docs/extensions/toc.md | 6 +++--- markdown/extensions/toc.py | 7 ++++--- tests/test_syntax/extensions/test_toc.py | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/extensions/toc.md b/docs/extensions/toc.md index 8e295a51b..33e0763aa 100644 --- a/docs/extensions/toc.md +++ b/docs/extensions/toc.md @@ -252,11 +252,11 @@ The following options are provided to configure the output: included in the Table of Contents. To exclude this first level, you have to set `toc_depth` to `"4-6"`. -* **`max_level`** - Define the highest heading level (deepest ``) for which to add anchor links or permalinks. +* **`link_depth`** + Add anchors/permalinks only to headings up to this depth (`h1`–`hN`). Defaults to `6`. - For example, setting max_level to `2` adds anchors only to `

` and `

` elements. + For example, setting `link_depth` to `2` adds anchors only to `

` and `

` elements. A trivial example: diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py index 22d1c75af..ea8e70626 100644 --- a/markdown/extensions/toc.py +++ b/markdown/extensions/toc.py @@ -257,7 +257,7 @@ def __init__(self, md: Markdown, config: dict[str, Any]): self.permalink_class: str = config["permalink_class"] self.permalink_title: str = config["permalink_title"] self.permalink_leading: bool | None = parseBoolValue(config["permalink_leading"], False) - self.header_rgx = re.compile(f'[Hh][1-{config["max_level"]}]') + self.header_rgx = re.compile(f'[Hh][1-{config["link_depth"]}]') if isinstance(config["toc_depth"], str) and '-' in config["toc_depth"]: self.toc_top, self.toc_bottom = [int(x) for x in config["toc_depth"].split('-')] else: @@ -466,9 +466,10 @@ def __init__(self, **kwargs): 'separated by a hyphen in between (`2-5`) defines the top (t) and the bottom (b) (..). ' 'Default: `6` (bottom).' ], - 'max_level': [ + 'link_depth': [ 6, - 'Define the max heading level for which to add anchors/permalinks.' + 'Add anchors/permalinks only to headings up to this depth (`h1`–`hN`). ' + 'For example, `link_depth: 2` adds them to `h1` and `h2` only. ' 'Default: `6`' ] } diff --git a/tests/test_syntax/extensions/test_toc.py b/tests/test_syntax/extensions/test_toc.py index 6259e7860..f5b6729ea 100644 --- a/tests/test_syntax/extensions/test_toc.py +++ b/tests/test_syntax/extensions/test_toc.py @@ -555,7 +555,7 @@ def testAnchorLinkWithDoubleInlineCode(self): extensions=[TocExtension(anchorlink=True)] ) - def testAnchorLinkWithMaxLevel(self): + def testAnchorLinkWithLinkDepth(self): self.assertMarkdownRenders( self.dedent( ''' @@ -570,7 +570,7 @@ def testAnchorLinkWithMaxLevel(self):

Header 2

''' ), - extensions=[TocExtension(anchorlink=True, max_level=1)] + extensions=[TocExtension(anchorlink=True, link_depth=1)] ) def testPermalink(self):