1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
from __future__ import annotations
import logging
import pathlib
import textwrap
from typing import Callable
from jinja2 import BaseLoader, Environment
__all__ = ["TemplateRewritingLoader"]
log = logging.getLogger(f"mkdocs.plugins.{__name__}")
class TemplateRewritingLoader(BaseLoader):
def __init__(self, loader: BaseLoader):
self.loader = loader
self.found_supported_theme = False
def get_source(
self, environment: Environment, template: str
) -> tuple[str, str, Callable[[], bool] | None]:
src: str | None
src, filename, uptodate = self.loader.get_source(environment, template)
old_src = src
assert filename is not None
path = pathlib.Path(filename).as_posix()
if path.endswith("/mkdocs/templates/sitemap.xml"):
src = _transform_mkdocs_sitemap_template(src)
else:
# the second path is used in MkDocs-Material >= 9.4
if path.endswith(
(
"/material/partials/nav-item.html",
"/material/templates/partials/nav-item.html",
),
):
src = _transform_material_nav_item_template(src)
elif path.endswith(
(
"/material/partials/tabs-item.html",
"/material/templates/partials/tabs-item.html",
),
):
src = _transform_material_tabs_item_template(src)
elif path.endswith("/themes/readthedocs/base.html"):
src = _transform_readthedocs_base_template(src)
elif path.endswith("/nature/base.html"):
src = None # Just works!
else:
return src, filename, uptodate
self.found_supported_theme = True
if old_src == src:
log.warning(
f"Failed to adapt the theme file '{filename}'. "
f"This is likely a bug in mkdocs-section-index, and things won't work as expected."
)
return src or old_src, filename, uptodate
def _transform_mkdocs_sitemap_template(src: str) -> str | None:
if " in pages " in src:
return None
# The below only for versions <= 1.1.2.
return src.replace(
"{%- else %}",
"{%- endif %}{% if item.url %}",
)
def _transform_material_nav_item_template(src: str) -> str:
if "navigation.indexes" in src:
return (
src.replace( # For versions >= 9.6.10
"_ = namespace(index = none)",
"_ = namespace(index = nav_item if nav_item.url else none)",
)
.replace( # For versions < 9.6.10
"{% set indexes = [] %}",
"{% set indexes = [nav_item] if nav_item.url else [] %}",
)
.replace(
"{% if nav_item.children | length > 1 %}",
"{% if nav_item.children %}",
)
)
# The above only for versions >= 7.3, the below only for versions < 7.3.
src = src.replace(
"{% if nav_item.children %}",
"{% if nav_item.children and not ('navigation.tabs' in features and level == 1 and not nav_item.active and nav_item.url) %}",
)
repl = """\
{% if nav_item.url %}
<a href="{{ nav_item.url | url }}" class="md-nav__link{% if nav_item == page %} md-nav__link--active{% endif %}"
style="margin: initial; padding: initial; pointer-events: initial">
{% endif %}
[...]
{% if nav_item.url %}</a>{% endif %}
"""
lines = src.split("\n")
for i, (line1, line2) in enumerate(zip(lines, lines[1:])):
for a, b in (line1, line2), (line2, line1):
if "md-nav__icon" in a and b.endswith("{{ nav_item.title }}"):
lines[i : i + 2] = (a, _replace_line(b, repl))
break
return "\n".join(lines)
def _transform_material_tabs_item_template(src: str) -> str:
src = src.replace(
"{% if first.children %}",
"{% if first.children and not first.url %}",
)
# The above only for versions >= 9.2
src = src.replace(
"{% if nav_item.children %}",
"{% if nav_item.children and not nav_item.url %}",
)
# The above only for versions > 6.1.7, the below only for versions <= 6.1.7.
return src.replace(
"(nav_item.children | first).url",
"(nav_item.url or (nav_item.children | first).url)",
).replace(
"if (nav_item.children | first).children",
"if (nav_item.children | first).children and not nav_item.url",
)
def _transform_readthedocs_base_template(src: str) -> str | None:
if " if nav_item.is_page " in src:
return None
# The below only for versions < 1.6:
repl = """\
{% if nav_item.url %}
<ul><li{% if nav_item == page %} class="current"{% endif %}>
<a href="{{ nav_item.url|url }}" style="padding: 0; font-size: inherit; line-height: inherit">
{% endif %}
[...]
{% if nav_item.url %}
</a>
</li></ul>
{% endif %}
"""
lines = src.split("\n")
for i, line in enumerate(lines):
if "{{ nav_item.title }}" in line:
lines[i] = _replace_line(lines[i], repl)
return "\n".join(lines)
def _replace_line(line: str, wrapper: str, new_line: str | None = None) -> str:
leading_space = line[: -len(line.lstrip())]
if new_line is None:
new_line = line.lstrip()
new_text = textwrap.dedent(wrapper.rstrip()).replace("[...]", new_line)
return textwrap.indent(new_text, leading_space)
|