zendoc 0.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.
- zendoc/__init__.py +16 -0
- zendoc/headings.py +127 -0
- zendoc/py.typed +0 -0
- zendoc/refs.py +138 -0
- zendoc/util.py +63 -0
- zendoc-0.1.0.dist-info/METADATA +92 -0
- zendoc-0.1.0.dist-info/RECORD +10 -0
- zendoc-0.1.0.dist-info/WHEEL +4 -0
- zendoc-0.1.0.dist-info/entry_points.txt +3 -0
- zendoc-0.1.0.dist-info/licenses/LICENSE +21 -0
zendoc/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright (c) 2026 Mark Buckwell and contributors
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""zendoc: a family of independent Python-Markdown extensions for section
|
|
5
|
+
cross-references and bibliography/citation handling, in the spirit of
|
|
6
|
+
pymdown-extensions - each is its own extension, enabled separately:
|
|
7
|
+
|
|
8
|
+
- ``zendoc.headings`` - heading ids and hierarchical section numbers.
|
|
9
|
+
- ``zendoc.refs`` - ``\\ref{id}`` section cross-references.
|
|
10
|
+
|
|
11
|
+
See https://github.com/buckwem/zendoc-extension for documentation.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
__all__ = ["__version__"]
|
zendoc/headings.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Copyright (c) 2026 Mark Buckwell and contributors
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""zendoc.headings: gives every heading an id and a hierarchical section
|
|
5
|
+
number, recorded in a shared :class:`~zendoc.util.IdRegistry` that other
|
|
6
|
+
zendoc extensions (currently :mod:`zendoc.refs`) look entries up in.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
import xml.etree.ElementTree as etree
|
|
13
|
+
|
|
14
|
+
from markdown import Markdown
|
|
15
|
+
from markdown.extensions import Extension
|
|
16
|
+
from markdown.extensions.toc import TocExtension
|
|
17
|
+
from markdown.treeprocessors import Treeprocessor
|
|
18
|
+
|
|
19
|
+
from zendoc.util import IdRegistry
|
|
20
|
+
|
|
21
|
+
HEADING_TAGS = {"h1", "h2", "h3", "h4", "h5", "h6"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _slugify(text: str) -> str:
|
|
25
|
+
"""Minimal fallback slug, used only when 'toc' hasn't already assigned an
|
|
26
|
+
id. Enable Python-Markdown's own 'toc' extension for slugs that match the
|
|
27
|
+
rest of a 'toc'-rendered document exactly (unicode handling, custom
|
|
28
|
+
separators, etc.) - this fallback exists only so the registry still works
|
|
29
|
+
if a caller genuinely doesn't want a table of contents.
|
|
30
|
+
"""
|
|
31
|
+
slug = re.sub(r"[^\w\s-]", "", text).strip().lower()
|
|
32
|
+
return re.sub(r"\s+", "-", slug)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class HeadingsTreeprocessor(Treeprocessor):
|
|
36
|
+
"""Records every h1-h6 element's id, and its hierarchical section number,
|
|
37
|
+
in a shared :class:`IdRegistry`, keyed by the current document's source
|
|
38
|
+
name.
|
|
39
|
+
|
|
40
|
+
Numbering is per-document: h1 is a top-level counter, h2 nests under the
|
|
41
|
+
nearest preceding h1 ("1.1", "1.2", ...), and so on through h6 - reset
|
|
42
|
+
from scratch on every call, so reordering headings within a document
|
|
43
|
+
always produces correct numbers on the next build. A heading with an
|
|
44
|
+
``unnumbered`` class (e.g. via ``# Title {: .unnumbered }``) is still
|
|
45
|
+
given an id but excluded from numbering - its counter position is
|
|
46
|
+
skipped entirely - so its registered ``number`` is ``None``.
|
|
47
|
+
|
|
48
|
+
Runs at a lower priority than 'toc' (registered at 5) so it always reads
|
|
49
|
+
the final id 'toc' assigned - including one already set explicitly via
|
|
50
|
+
'attr_list' - rather than racing it.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, md: Markdown, registry: IdRegistry, source: str) -> None:
|
|
54
|
+
super().__init__(md)
|
|
55
|
+
self.registry = registry
|
|
56
|
+
self.source = source
|
|
57
|
+
|
|
58
|
+
def run(self, root: etree.Element) -> None:
|
|
59
|
+
self.registry.clear_source(self.source)
|
|
60
|
+
counters = [0] * 6
|
|
61
|
+
for el in root.iter():
|
|
62
|
+
if el.tag not in HEADING_TAGS:
|
|
63
|
+
continue
|
|
64
|
+
text = "".join(el.itertext())
|
|
65
|
+
heading_id = el.get("id")
|
|
66
|
+
if not heading_id:
|
|
67
|
+
heading_id = _slugify(text)
|
|
68
|
+
el.set("id", heading_id)
|
|
69
|
+
|
|
70
|
+
level = int(el.tag[1])
|
|
71
|
+
classes = (el.get("class") or "").split()
|
|
72
|
+
if "unnumbered" in classes:
|
|
73
|
+
number = None
|
|
74
|
+
else:
|
|
75
|
+
counters[level - 1] += 1
|
|
76
|
+
for deeper in range(level, 6):
|
|
77
|
+
counters[deeper] = 0
|
|
78
|
+
number = ".".join(str(c) for c in counters[:level])
|
|
79
|
+
|
|
80
|
+
self.registry.register(
|
|
81
|
+
source=self.source,
|
|
82
|
+
id=heading_id,
|
|
83
|
+
level=level,
|
|
84
|
+
text=text,
|
|
85
|
+
number=number,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class HeadingsExtension(Extension):
|
|
90
|
+
"""Python-Markdown extension assigning ids and section numbers to headings."""
|
|
91
|
+
|
|
92
|
+
def __init__(self, **kwargs: object) -> None:
|
|
93
|
+
# Popped rather than run through Extension's own config/setConfig:
|
|
94
|
+
# that machinery bool-coerces any config value whose *current*
|
|
95
|
+
# default is None (see markdown.util.parseBoolValue), which would
|
|
96
|
+
# silently corrupt a real IdRegistry object passed in explicitly.
|
|
97
|
+
registry = kwargs.pop("registry", None)
|
|
98
|
+
self.registry: IdRegistry = (
|
|
99
|
+
registry if isinstance(registry, IdRegistry) else IdRegistry()
|
|
100
|
+
)
|
|
101
|
+
self.config = {
|
|
102
|
+
"source": [
|
|
103
|
+
"",
|
|
104
|
+
"Identifier for the current document (e.g. its path), used "
|
|
105
|
+
"to scope this document's own entries in the registry.",
|
|
106
|
+
],
|
|
107
|
+
}
|
|
108
|
+
super().__init__(**kwargs)
|
|
109
|
+
|
|
110
|
+
def extendMarkdown(self, md: Markdown) -> None:
|
|
111
|
+
md.registerExtension(self)
|
|
112
|
+
# Heading ids are 'toc''s job (including respecting one 'attr_list'
|
|
113
|
+
# already set) - reuse it rather than re-deriving slugs here, but
|
|
114
|
+
# don't clobber a caller's own 'toc' config (e.g. permalink=True) if
|
|
115
|
+
# they've already enabled it themselves.
|
|
116
|
+
if "toc" not in md.treeprocessors:
|
|
117
|
+
TocExtension().extendMarkdown(md)
|
|
118
|
+
source: str = self.getConfig("source")
|
|
119
|
+
md.treeprocessors.register(
|
|
120
|
+
HeadingsTreeprocessor(md, self.registry, source),
|
|
121
|
+
"zendoc-headings",
|
|
122
|
+
4,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def makeExtension(**kwargs: object) -> HeadingsExtension:
|
|
127
|
+
return HeadingsExtension(**kwargs)
|
zendoc/py.typed
ADDED
|
File without changes
|
zendoc/refs.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright (c) 2026 Mark Buckwell and contributors
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""zendoc.refs: ``\\ref{id}`` section cross-reference syntax, resolving to
|
|
5
|
+
the referenced heading's current section number - similar in spirit to
|
|
6
|
+
LaTeX's ``\\ref``. Builds on the id registry from :mod:`zendoc.headings`,
|
|
7
|
+
which is auto-enabled with matching defaults if not already present.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import xml.etree.ElementTree as etree
|
|
14
|
+
|
|
15
|
+
from markdown import Markdown
|
|
16
|
+
from markdown.extensions import Extension
|
|
17
|
+
from markdown.inlinepatterns import InlineProcessor
|
|
18
|
+
from markdown.treeprocessors import Treeprocessor
|
|
19
|
+
|
|
20
|
+
from zendoc.headings import HeadingsExtension
|
|
21
|
+
from zendoc.util import IdRegistry
|
|
22
|
+
|
|
23
|
+
REF_RE = r"\\ref\{([^}\s]+)\}"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RefInlineProcessor(InlineProcessor):
|
|
27
|
+
"""Matches ``\\ref{id}`` and emits an unresolved placeholder ``<a>``
|
|
28
|
+
carrying the referenced id in a ``data-zendoc-ref`` attribute.
|
|
29
|
+
|
|
30
|
+
Registered at a low inline-pattern priority so it runs after 'backtick'
|
|
31
|
+
(190) and 'escape' (180) - meaning inline code spans are already stashed
|
|
32
|
+
out of reach by the time this pattern runs, so ``\\ref{...}`` shown as
|
|
33
|
+
literal example syntax inside `` `code` `` survives untouched, the same
|
|
34
|
+
protection fenced code blocks already get from being stashed even
|
|
35
|
+
earlier, during preprocessing.
|
|
36
|
+
|
|
37
|
+
The placeholder can't be resolved to a real section number here: inline
|
|
38
|
+
patterns run before the current document's own headings have been
|
|
39
|
+
numbered (see HeadingsTreeprocessor, priority 4, which runs after this
|
|
40
|
+
pattern's containing 'inline' treeprocessor, priority 20) - resolution
|
|
41
|
+
happens later, in RefResolverTreeprocessor.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def handleMatch( # type: ignore[override]
|
|
45
|
+
self, m: re.Match[str], data: str
|
|
46
|
+
) -> tuple[etree.Element, int, int]:
|
|
47
|
+
el = etree.Element("a")
|
|
48
|
+
el.set("data-zendoc-ref", m.group(1))
|
|
49
|
+
el.set("class", "zendoc-ref")
|
|
50
|
+
return el, m.start(0), m.end(0)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class RefResolverTreeprocessor(Treeprocessor):
|
|
54
|
+
"""Resolves the placeholder ``<a data-zendoc-ref="id">`` elements left by
|
|
55
|
+
:class:`RefInlineProcessor` to the referenced heading's section number,
|
|
56
|
+
once the current document's own headings have been numbered.
|
|
57
|
+
|
|
58
|
+
Runs at a lower priority than 'zendoc-headings' (4) so every heading in
|
|
59
|
+
*this* document - including one defined further down the page than
|
|
60
|
+
where it's referenced - is already registered by the time resolution
|
|
61
|
+
happens. A reference to a heading in a document not yet processed in
|
|
62
|
+
this build (e.g. a later page in a multi-page site) can't be resolved
|
|
63
|
+
yet either; both cases fall back to `unresolved`, the same way an
|
|
64
|
+
undefined LaTeX \\ref shows "??" until a later compilation pass.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, md: Markdown, registry: IdRegistry, unresolved: str = "??") -> None:
|
|
68
|
+
super().__init__(md)
|
|
69
|
+
self.registry = registry
|
|
70
|
+
self.unresolved = unresolved
|
|
71
|
+
|
|
72
|
+
def run(self, root: etree.Element) -> None:
|
|
73
|
+
for el in root.iter("a"):
|
|
74
|
+
ref_id = el.get("data-zendoc-ref")
|
|
75
|
+
if ref_id is None:
|
|
76
|
+
continue
|
|
77
|
+
del el.attrib["data-zendoc-ref"]
|
|
78
|
+
record = self.registry.get(ref_id)
|
|
79
|
+
if record is None or record.number is None:
|
|
80
|
+
el.text = self.unresolved
|
|
81
|
+
el.set("class", "zendoc-ref zendoc-ref-unresolved")
|
|
82
|
+
if record is not None:
|
|
83
|
+
# Known heading, just unnumbered (e.g. {: .unnumbered }) -
|
|
84
|
+
# still a valid link target, unlike a genuinely unknown id.
|
|
85
|
+
el.set("href", f"#{ref_id}")
|
|
86
|
+
else:
|
|
87
|
+
el.text = record.number
|
|
88
|
+
el.set("href", f"#{ref_id}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class RefsExtension(Extension):
|
|
92
|
+
"""Python-Markdown extension providing the ``\\ref{id}`` syntax."""
|
|
93
|
+
|
|
94
|
+
def __init__(self, **kwargs: object) -> None:
|
|
95
|
+
# See HeadingsExtension for why this is popped rather than run
|
|
96
|
+
# through Extension's own config/setConfig machinery. None here
|
|
97
|
+
# means "discover the registry from a sibling HeadingsExtension",
|
|
98
|
+
# not "use an empty registry" - that distinction can't be made once
|
|
99
|
+
# a value has round-tripped through setConfig.
|
|
100
|
+
registry = kwargs.pop("registry", None)
|
|
101
|
+
self.registry: IdRegistry | None = registry if isinstance(registry, IdRegistry) else None
|
|
102
|
+
self.config = {
|
|
103
|
+
"unresolved": [
|
|
104
|
+
"??",
|
|
105
|
+
"Text rendered by \\ref{id} when id doesn't resolve to a "
|
|
106
|
+
"numbered heading - unknown id, or a heading marked "
|
|
107
|
+
"unnumbered.",
|
|
108
|
+
],
|
|
109
|
+
}
|
|
110
|
+
super().__init__(**kwargs)
|
|
111
|
+
|
|
112
|
+
def extendMarkdown(self, md: Markdown) -> None:
|
|
113
|
+
md.registerExtension(self)
|
|
114
|
+
registry = self.registry
|
|
115
|
+
if registry is None:
|
|
116
|
+
headings_ext = next(
|
|
117
|
+
(ext for ext in md.registeredExtensions if isinstance(ext, HeadingsExtension)),
|
|
118
|
+
None,
|
|
119
|
+
)
|
|
120
|
+
if headings_ext is None:
|
|
121
|
+
headings_ext = HeadingsExtension()
|
|
122
|
+
headings_ext.extendMarkdown(md)
|
|
123
|
+
registry = headings_ext.registry
|
|
124
|
+
unresolved: str = self.getConfig("unresolved")
|
|
125
|
+
md.inlinePatterns.register(
|
|
126
|
+
RefInlineProcessor(REF_RE, md),
|
|
127
|
+
"zendoc-ref",
|
|
128
|
+
45,
|
|
129
|
+
)
|
|
130
|
+
md.treeprocessors.register(
|
|
131
|
+
RefResolverTreeprocessor(md, registry, unresolved),
|
|
132
|
+
"zendoc-ref-resolver",
|
|
133
|
+
2,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def makeExtension(**kwargs: object) -> RefsExtension:
|
|
138
|
+
return RefsExtension(**kwargs)
|
zendoc/util.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Copyright (c) 2026 Mark Buckwell and contributors
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""Shared data structures used by more than one zendoc extension.
|
|
5
|
+
|
|
6
|
+
A single :class:`IdRegistry` instance is meant to be shared across every
|
|
7
|
+
source document in a build (one extension instance per document, e.g. one
|
|
8
|
+
:class:`~zendoc.headings.HeadingsExtension` call per page), so that
|
|
9
|
+
:mod:`zendoc.refs` (and a future citation extension) can resolve an id to
|
|
10
|
+
the document, heading, and current section number that defines it,
|
|
11
|
+
regardless of which document is currently being converted.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class HeadingRecord:
|
|
21
|
+
source: str
|
|
22
|
+
id: str
|
|
23
|
+
level: int
|
|
24
|
+
text: str
|
|
25
|
+
number: str | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DuplicateIdError(ValueError):
|
|
29
|
+
"""Raised when the same id is registered from two different sources."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class IdRegistry:
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
self._headings: dict[str, HeadingRecord] = {}
|
|
35
|
+
|
|
36
|
+
def register(
|
|
37
|
+
self, source: str, id: str, level: int, text: str, number: str | None = None
|
|
38
|
+
) -> None:
|
|
39
|
+
existing = self._headings.get(id)
|
|
40
|
+
if existing is not None and existing.source != source:
|
|
41
|
+
raise DuplicateIdError(
|
|
42
|
+
f"heading id {id!r} is already registered from "
|
|
43
|
+
f"{existing.source!r}; cannot also register it from {source!r}"
|
|
44
|
+
)
|
|
45
|
+
self._headings[id] = HeadingRecord(
|
|
46
|
+
source=source, id=id, level=level, text=text, number=number
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def get(self, id: str) -> HeadingRecord | None:
|
|
50
|
+
return self._headings.get(id)
|
|
51
|
+
|
|
52
|
+
def __contains__(self, id: str) -> bool:
|
|
53
|
+
return id in self._headings
|
|
54
|
+
|
|
55
|
+
def clear_source(self, source: str) -> None:
|
|
56
|
+
"""Drops every entry previously registered from source.
|
|
57
|
+
|
|
58
|
+
Needed so re-converting the same document (e.g. a live-reload dev
|
|
59
|
+
server) can't leave a stale id behind after a heading's text - and
|
|
60
|
+
therefore its slug - changes between builds.
|
|
61
|
+
"""
|
|
62
|
+
for stale_id in [k for k, v in self._headings.items() if v.source == source]:
|
|
63
|
+
del self._headings[stale_id]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zendoc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A family of Python-Markdown extensions for section cross-references and bibliography/citation handling
|
|
5
|
+
Project-URL: Homepage, https://github.com/buckwem/zendoc-extension
|
|
6
|
+
Project-URL: Issues, https://github.com/buckwem/zendoc-extension/issues
|
|
7
|
+
Author: Mark Buckwell
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: markdown>=3.4
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
22
|
+
Requires-Dist: types-markdown; extra == 'dev'
|
|
23
|
+
Provides-Extra: docs
|
|
24
|
+
Requires-Dist: zensical; extra == 'docs'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# zendoc
|
|
28
|
+
|
|
29
|
+
A family of [Python-Markdown](https://python-markdown.github.io/) extensions
|
|
30
|
+
for section cross-references and bibliography/citation handling, in the
|
|
31
|
+
spirit of [pymdown-extensions](https://facelessuser.github.io/pymdown-extensions/):
|
|
32
|
+
each extension is independent and enabled separately. Factored out of
|
|
33
|
+
[zendoc-template](https://github.com/buckwem/zendoc-template) so it can be
|
|
34
|
+
installed and reused independently of that template.
|
|
35
|
+
|
|
36
|
+
> **Status:** early - `zendoc.headings` and `zendoc.refs` are implemented;
|
|
37
|
+
> citation handling isn't yet. See
|
|
38
|
+
> [zendoc-template#25](https://github.com/buckwem/zendoc-template/issues/25)
|
|
39
|
+
> for the tracking issue and scope.
|
|
40
|
+
|
|
41
|
+
**[Full documentation](https://buckwem.github.io/zendoc-extension/)**
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install zendoc
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Extensions
|
|
50
|
+
|
|
51
|
+
| Extension | Description |
|
|
52
|
+
|---|---|
|
|
53
|
+
| [`zendoc.headings`](https://buckwem.github.io/zendoc-extension/extensions/headings/) | Gives every heading an id and a hierarchical section number ("1", "1.1", "1.2", "2", ...). |
|
|
54
|
+
| [`zendoc.refs`](https://buckwem.github.io/zendoc-extension/extensions/refs/) | `\ref{id}` section cross-references, resolving to the target's current number - similar in spirit to LaTeX's `\ref`. |
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import markdown
|
|
58
|
+
|
|
59
|
+
html = markdown.markdown(text, extensions=["zendoc.headings", "zendoc.refs"])
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```md
|
|
63
|
+
# Introduction {: #intro }
|
|
64
|
+
|
|
65
|
+
See \ref{intro} for background.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`\ref{intro}` resolves to a link reading `1` - the heading's current
|
|
69
|
+
section number - and stays correct if sections are reordered, since
|
|
70
|
+
numbering is recomputed on every conversion. See the
|
|
71
|
+
[docs](https://buckwem.github.io/zendoc-extension/) for options, multi-page
|
|
72
|
+
registry sharing, and full syntax details.
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
python -m venv .venv
|
|
78
|
+
source .venv/bin/activate
|
|
79
|
+
pip install -e ".[dev]"
|
|
80
|
+
pytest
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
To build the documentation locally:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install -e ".[docs]"
|
|
87
|
+
zensical serve
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT - see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
zendoc/__init__.py,sha256=dzzQyw5WajnTEbrH2wyK4e4rXtpngpM95xqwgg3khNA,549
|
|
2
|
+
zendoc/headings.py,sha256=do5GimUOf5fLAoYL3B26BOaPH5vLG7t8Tz39v1I27Pk,4911
|
|
3
|
+
zendoc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
zendoc/refs.py,sha256=Ad6clNFnU0_tWNC8-ixfQ196Xj3471Pnbf5Sr_C1uwY,5650
|
|
5
|
+
zendoc/util.py,sha256=WNrbKomO3gh5khiwpWd0t0XGV0CE5i5DHZDGgYj_2EI,2169
|
|
6
|
+
zendoc-0.1.0.dist-info/METADATA,sha256=t1vwqp-iGcInDcBMhiT_IgHt_07JT0moZVl-PRbPkqE,2941
|
|
7
|
+
zendoc-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
8
|
+
zendoc-0.1.0.dist-info/entry_points.txt,sha256=cyid0A-qxc432LeM9dCznLYDYf1ldgswCpbDEt_2ShY,114
|
|
9
|
+
zendoc-0.1.0.dist-info/licenses/LICENSE,sha256=qk54ayQf53MkX-HvpfOn2dFrvVVImmpvdYaKjPv2B-U,1087
|
|
10
|
+
zendoc-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Buckwell and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|