mkdocs-nav-numbering-plugin 0.1.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mkdocs_nav_numbering_plugin/nav_numbering.py +87 -1
- {mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/METADATA +26 -6
- mkdocs_nav_numbering_plugin-1.1.0.dist-info/RECORD +8 -0
- {mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/licenses/LICENSE +1 -1
- mkdocs_nav_numbering_plugin-0.1.0.dist-info/RECORD +0 -8
- {mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/WHEEL +0 -0
- {mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/entry_points.txt +0 -0
- {mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
+
import unicodedata
|
|
2
3
|
from typing import Dict, List
|
|
3
4
|
|
|
4
5
|
from mkdocs.config import config_options
|
|
@@ -6,6 +7,39 @@ from mkdocs.plugins import BasePlugin
|
|
|
6
7
|
from mkdocs.structure.nav import Navigation, Section, Page, Link
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
def slugify(text: str) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Convert text to a URL-friendly slug, matching MkDocs' default toc behavior.
|
|
13
|
+
|
|
14
|
+
- Converts to lowercase
|
|
15
|
+
- Normalizes unicode characters
|
|
16
|
+
- Replaces spaces and underscores with hyphens
|
|
17
|
+
- Removes non-alphanumeric characters (except hyphens)
|
|
18
|
+
- Collapses multiple hyphens into one
|
|
19
|
+
- Strips leading/trailing hyphens
|
|
20
|
+
"""
|
|
21
|
+
# Normalize unicode characters (e.g., accented chars -> base form)
|
|
22
|
+
text = unicodedata.normalize("NFKD", text)
|
|
23
|
+
text = text.encode("ascii", "ignore").decode("ascii")
|
|
24
|
+
|
|
25
|
+
# Convert to lowercase
|
|
26
|
+
text = text.lower()
|
|
27
|
+
|
|
28
|
+
# Replace spaces and underscores with hyphens
|
|
29
|
+
text = re.sub(r"[\s_]+", "-", text)
|
|
30
|
+
|
|
31
|
+
# Remove non-alphanumeric characters except hyphens
|
|
32
|
+
text = re.sub(r"[^\w-]", "", text)
|
|
33
|
+
|
|
34
|
+
# Collapse multiple hyphens into one
|
|
35
|
+
text = re.sub(r"-+", "-", text)
|
|
36
|
+
|
|
37
|
+
# Strip leading/trailing hyphens
|
|
38
|
+
text = text.strip("-")
|
|
39
|
+
|
|
40
|
+
return text
|
|
41
|
+
|
|
42
|
+
|
|
9
43
|
class NavNumberingPlugin(BasePlugin):
|
|
10
44
|
"""
|
|
11
45
|
MkDocs plugin to add hierarchical numbering to navigation items and page headings.
|
|
@@ -19,6 +53,7 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
19
53
|
number_h1: Add number to the first h1 (page title) (default: true)
|
|
20
54
|
separator: Separator between number parts (default: ".")
|
|
21
55
|
exclude: List of page paths to exclude from numbering (default: [])
|
|
56
|
+
preserve_anchor_ids: Preserve original anchor IDs without number prefixes (default: false)
|
|
22
57
|
"""
|
|
23
58
|
|
|
24
59
|
config_scheme = (
|
|
@@ -30,6 +65,7 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
30
65
|
("number_h1", config_options.Type(bool, default=True)),
|
|
31
66
|
("separator", config_options.Type(str, default=".")),
|
|
32
67
|
("exclude", config_options.Type(list, default=[])),
|
|
68
|
+
("preserve_anchor_ids", config_options.Type(bool, default=False)),
|
|
33
69
|
)
|
|
34
70
|
|
|
35
71
|
def __init__(self):
|
|
@@ -97,6 +133,9 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
97
133
|
Prepend the nav number to headings inside each page.
|
|
98
134
|
- The first h1 (page title) gets the base_number (e.g., 3.1.1)
|
|
99
135
|
- Subsequent h2, h3, etc. get sub-numbers (e.g., 3.1.1.1, 3.1.1.2)
|
|
136
|
+
|
|
137
|
+
When preserve_anchor_ids is enabled, explicit {#slug} attributes are added
|
|
138
|
+
to headings to preserve the original anchor IDs without number prefixes.
|
|
100
139
|
"""
|
|
101
140
|
if not self.config["enabled"] or not self.config["number_headings"]:
|
|
102
141
|
return markdown
|
|
@@ -109,6 +148,7 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
109
148
|
heading_depth = self.config["heading_depth"]
|
|
110
149
|
number_h1 = self.config["number_h1"]
|
|
111
150
|
separator = self.config["separator"]
|
|
151
|
+
preserve_anchor_ids = self.config["preserve_anchor_ids"]
|
|
112
152
|
base_depth = len(base_number.split(separator))
|
|
113
153
|
|
|
114
154
|
# Track whether we've seen the first h1 (page title)
|
|
@@ -118,11 +158,42 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
118
158
|
# h2 -> counter[0], h3 -> counter[1], etc.
|
|
119
159
|
counters: List[int] = []
|
|
120
160
|
|
|
161
|
+
# Track used slugs for duplicate detection (per page)
|
|
162
|
+
used_slugs: Dict[str, int] = {}
|
|
163
|
+
|
|
164
|
+
def get_unique_slug(title: str) -> str:
|
|
165
|
+
"""Generate a unique slug, adding suffix for duplicates."""
|
|
166
|
+
base_slug = slugify(title)
|
|
167
|
+
if not base_slug:
|
|
168
|
+
base_slug = "heading"
|
|
169
|
+
|
|
170
|
+
if base_slug not in used_slugs:
|
|
171
|
+
used_slugs[base_slug] = 0
|
|
172
|
+
return base_slug
|
|
173
|
+
else:
|
|
174
|
+
used_slugs[base_slug] += 1
|
|
175
|
+
return f"{base_slug}-{used_slugs[base_slug]}"
|
|
176
|
+
|
|
177
|
+
def extract_existing_id(title: str) -> tuple:
|
|
178
|
+
"""
|
|
179
|
+
Extract existing {#custom-id} from title if present.
|
|
180
|
+
Returns (clean_title, existing_id) or (title, None).
|
|
181
|
+
"""
|
|
182
|
+
match = re.search(r'\s*\{#([^}]+)\}\s*$', title)
|
|
183
|
+
if match:
|
|
184
|
+
existing_id = match.group(1)
|
|
185
|
+
clean_title = title[:match.start()].strip()
|
|
186
|
+
return clean_title, existing_id
|
|
187
|
+
return title, None
|
|
188
|
+
|
|
121
189
|
def repl(match: re.Match) -> str:
|
|
122
190
|
hashes = match.group("hashes")
|
|
123
|
-
|
|
191
|
+
raw_title = match.group("title").strip()
|
|
124
192
|
level = len(hashes) # "#"=1, "##"=2, etc.
|
|
125
193
|
|
|
194
|
+
# Check for existing {#custom-id} attribute
|
|
195
|
+
title, existing_id = extract_existing_id(raw_title)
|
|
196
|
+
|
|
126
197
|
# Avoid double-numbering if already numbered
|
|
127
198
|
if re.match(r"^\d+(\.\d+)*\s+", title):
|
|
128
199
|
return match.group(0)
|
|
@@ -132,12 +203,22 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
132
203
|
if not first_h1_seen[0]:
|
|
133
204
|
first_h1_seen[0] = True
|
|
134
205
|
if number_h1:
|
|
206
|
+
if preserve_anchor_ids and not existing_id:
|
|
207
|
+
slug = get_unique_slug(title)
|
|
208
|
+
return f"{hashes} {base_number} {title} {{#{slug}}}"
|
|
209
|
+
elif existing_id:
|
|
210
|
+
return f"{hashes} {base_number} {title} {{#{existing_id}}}"
|
|
135
211
|
return f"{hashes} {base_number} {title}"
|
|
136
212
|
return match.group(0)
|
|
137
213
|
else:
|
|
138
214
|
# Additional h1s in the same page - handle gracefully
|
|
139
215
|
counters.clear()
|
|
140
216
|
if number_h1:
|
|
217
|
+
if preserve_anchor_ids and not existing_id:
|
|
218
|
+
slug = get_unique_slug(title)
|
|
219
|
+
return f"{hashes} {base_number} {title} {{#{slug}}}"
|
|
220
|
+
elif existing_id:
|
|
221
|
+
return f"{hashes} {base_number} {title} {{#{existing_id}}}"
|
|
141
222
|
return f"{hashes} {base_number} {title}"
|
|
142
223
|
return match.group(0)
|
|
143
224
|
|
|
@@ -162,6 +243,11 @@ class NavNumberingPlugin(BasePlugin):
|
|
|
162
243
|
rel = separator.join(str(c) for c in counters[:adjusted_level])
|
|
163
244
|
full_number = f"{base_number}{separator}{rel}"
|
|
164
245
|
|
|
246
|
+
if preserve_anchor_ids and not existing_id:
|
|
247
|
+
slug = get_unique_slug(title)
|
|
248
|
+
return f"{hashes} {full_number} {title} {{#{slug}}}"
|
|
249
|
+
elif existing_id:
|
|
250
|
+
return f"{hashes} {full_number} {title} {{#{existing_id}}}"
|
|
165
251
|
return f"{hashes} {full_number} {title}"
|
|
166
252
|
|
|
167
253
|
heading_pattern = re.compile(r"^(?P<hashes>#{1,6})\s+(?P<title>.+)$", re.MULTILINE)
|
{mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/METADATA
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkdocs-nav-numbering-plugin
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: MkDocs plugin to add hierarchical numbering to nav and page headings
|
|
5
|
-
Author:
|
|
5
|
+
Author: Matus Drobuliak
|
|
6
6
|
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
-
Project-URL: Repository, https://github.com/
|
|
9
|
-
Project-URL: Issues, https://github.com/
|
|
7
|
+
Project-URL: Homepage, https://github.com/matusdrobuliak66/mkdocs-nav-numbering-plugin
|
|
8
|
+
Project-URL: Repository, https://github.com/matusdrobuliak66/mkdocs-nav-numbering-plugin
|
|
9
|
+
Project-URL: Issues, https://github.com/matusdrobuliak66/mkdocs-nav-numbering-plugin/issues
|
|
10
10
|
Keywords: mkdocs,plugin,navigation,numbering,documentation
|
|
11
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Environment :: Plugins
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -51,9 +51,13 @@ plugins:
|
|
|
51
51
|
number_h1: true
|
|
52
52
|
number_nav: true
|
|
53
53
|
number_headings: true
|
|
54
|
+
preserve_anchor_ids: false
|
|
54
55
|
separator: "."
|
|
55
56
|
exclude:
|
|
56
57
|
- index.md
|
|
58
|
+
|
|
59
|
+
markdown_extensions:
|
|
60
|
+
- attr_list # Required when preserve_anchor_ids is true
|
|
57
61
|
```
|
|
58
62
|
|
|
59
63
|
## Options
|
|
@@ -64,5 +68,21 @@ plugins:
|
|
|
64
68
|
- `number_nav` (bool, default: true)
|
|
65
69
|
- `number_headings` (bool, default: true)
|
|
66
70
|
- `number_h1` (bool, default: true)
|
|
71
|
+
- `preserve_anchor_ids` (bool, default: false) — Preserve original heading anchor IDs without number prefixes (requires `attr_list`)
|
|
67
72
|
- `separator` (str, default: ".")
|
|
68
73
|
- `exclude` (list, default: [])
|
|
74
|
+
|
|
75
|
+
### `preserve_anchor_ids`
|
|
76
|
+
|
|
77
|
+
By default, MkDocs generates heading IDs from the final heading text. Since this plugin prepends numbering, your anchors can end up including the numbers.
|
|
78
|
+
|
|
79
|
+
Enable `preserve_anchor_ids: true` to add explicit `{#...}` IDs based on the original (un-numbered) heading text. Duplicate headings are automatically suffixed with `-1`, `-2`, etc.
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
plugins:
|
|
83
|
+
- nav-numbering:
|
|
84
|
+
preserve_anchor_ids: true
|
|
85
|
+
|
|
86
|
+
markdown_extensions:
|
|
87
|
+
- attr_list
|
|
88
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
mkdocs_nav_numbering_plugin/__init__.py,sha256=McZEZwPtatKF0PvPVKMD7_nBeaOjdHNME6tQNwNIt8M,80
|
|
2
|
+
mkdocs_nav_numbering_plugin/nav_numbering.py,sha256=AC6QEk7muwKj-BwklfAZJXGsmfKIqb4xLZodpJmPW6M,10779
|
|
3
|
+
mkdocs_nav_numbering_plugin-1.1.0.dist-info/licenses/LICENSE,sha256=3xENzADezoOh8w__sH4AAF-e-757l_y4sooIAwjqpL0,1072
|
|
4
|
+
mkdocs_nav_numbering_plugin-1.1.0.dist-info/METADATA,sha256=pEGEBi9VxtxinF__oATLP2c2mQ5WjJWhAt4FZIZGnuI,2760
|
|
5
|
+
mkdocs_nav_numbering_plugin-1.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
6
|
+
mkdocs_nav_numbering_plugin-1.1.0.dist-info/entry_points.txt,sha256=NnO7O_09mMyAHFIspg2zmsfri_m7nt8Km8xZK0tgvtQ,80
|
|
7
|
+
mkdocs_nav_numbering_plugin-1.1.0.dist-info/top_level.txt,sha256=UcRwMarClerK-Iuj0RooOAq7IVhn_oRmQRiCZgSCfN4,28
|
|
8
|
+
mkdocs_nav_numbering_plugin-1.1.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
mkdocs_nav_numbering_plugin/__init__.py,sha256=McZEZwPtatKF0PvPVKMD7_nBeaOjdHNME6tQNwNIt8M,80
|
|
2
|
-
mkdocs_nav_numbering_plugin/nav_numbering.py,sha256=zRiG9_AZdLKe21pnb_b_vQSw8LkWpL8HK6UdY14WoPc,7263
|
|
3
|
-
mkdocs_nav_numbering_plugin-0.1.0.dist-info/licenses/LICENSE,sha256=o8hK_UrJ1kd51VuQ5ZP4d8gwK5WPlt6Zg5vO-T42eoo,1078
|
|
4
|
-
mkdocs_nav_numbering_plugin-0.1.0.dist-info/METADATA,sha256=Erwhh43scMWp4QOY-uGORxkA-5TPRs88iZfNoqqoDew,2005
|
|
5
|
-
mkdocs_nav_numbering_plugin-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
6
|
-
mkdocs_nav_numbering_plugin-0.1.0.dist-info/entry_points.txt,sha256=NnO7O_09mMyAHFIspg2zmsfri_m7nt8Km8xZK0tgvtQ,80
|
|
7
|
-
mkdocs_nav_numbering_plugin-0.1.0.dist-info/top_level.txt,sha256=UcRwMarClerK-Iuj0RooOAq7IVhn_oRmQRiCZgSCfN4,28
|
|
8
|
-
mkdocs_nav_numbering_plugin-0.1.0.dist-info/RECORD,,
|
{mkdocs_nav_numbering_plugin-0.1.0.dist-info → mkdocs_nav_numbering_plugin-1.1.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|