86 lines
3.6 KiB
Python
86 lines
3.6 KiB
Python
# https://github.com/squidfunk/mkdocs-material/blob/master/src/overrides/hooks/shortcodes.py
|
||
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
import unicodedata
|
||
from html import escape
|
||
|
||
from markdown.extensions import Extension
|
||
from markdown.preprocessors import Preprocessor
|
||
|
||
SHORTCODE_RE = re.compile(r"<!--\s*md:(env|arg|config|version|default|beta)\s*(.*?)\s*-->", re.I)
|
||
|
||
# Страница с описанием конфигурации. Указана явно, потому что в проекте
|
||
# нет страницы `/iptvc/config` — соответствующий раздел живёт в
|
||
# `content/common/config/config.md`. С `use_directory_urls = false` конечные
|
||
# ссылки должны включать `.html`.
|
||
CONFIG_PAGE = "/common/config/config.html"
|
||
|
||
|
||
class BadgePreprocessor(Preprocessor):
|
||
def run(self, lines: list[str]) -> list[str]:
|
||
return [SHORTCODE_RE.sub(self._replace, line) for line in lines]
|
||
|
||
def _replace(self, match: re.Match[str]) -> str:
|
||
kind, args = match.groups()
|
||
args = args.strip()
|
||
|
||
if kind == "env":
|
||
value = args or "(нет)"
|
||
return _badge(":vscode-symbol-variable:", f"`{value}`", 'Переменная окружения')
|
||
|
||
if kind == "arg":
|
||
value = args or "(нет)"
|
||
return _badge(":vscode-terminal:", f"`{value}`", 'Аргумент командной строки')
|
||
|
||
if kind == "config":
|
||
value = args or "(нет)"
|
||
anchor = _slugify(args)
|
||
return _badge(":material-cog-outline:", f"[`{value}`]({CONFIG_PAGE}#{anchor})", 'Параметр файла конфигурации')
|
||
|
||
if kind == "version":
|
||
return _badge(":material-tag-outline:", escape(args), 'Версия, в которой появился этот функционал')
|
||
|
||
if kind == "default":
|
||
value = escape(args or "null")
|
||
return _badge(":material-water-outline:", f"`{value}`", 'Значение по умолчанию')
|
||
|
||
if kind == "beta":
|
||
return _badge(":material-beta:", '', 'Экспериментальный функционал')
|
||
|
||
return match.group(0)
|
||
|
||
|
||
# Slugify, совместимый с дефолтным slugify в pymdownx.slugs:
|
||
# NFKD-нормализация → отбрасывание не-ASCII → замена не-alphanumeric на дефис →
|
||
# склейка дефисов → trim → lowercase. Позволяет заранее предсказать якорь,
|
||
# не дожидаясь обработки Markdown
|
||
def _slugify(value: str) -> str:
|
||
if not value:
|
||
return ""
|
||
normalized = unicodedata.normalize("NFKD", value)
|
||
ascii_only = normalized.encode("ascii", "ignore").decode("ascii")
|
||
slug = re.sub(r"[^a-zA-Z0-9]+", "-", ascii_only)
|
||
return slug.strip("-").lower()
|
||
|
||
|
||
# Формирует HTML-разметку бейджа. Иконки остаются Zensical-шорткодами
|
||
# и рендерятся штатным `pymdownx.emoji` на следующем этапе обработки Markdown.
|
||
def _badge(icon: str, text: str = "", title: str = "") -> str:
|
||
return "".join([
|
||
f'<span class="mdx-badge" title="{title}">',
|
||
f'<span class="mdx-badge__icon">{icon}</span>',
|
||
*([f'<span class="mdx-badge__text">{text}</span>'] if text else []),
|
||
'</span>',
|
||
])
|
||
|
||
|
||
class InlineBadgeExtension(Extension):
|
||
def extendMarkdown(self, md):
|
||
md.preprocessors.register(BadgePreprocessor(md), "inline_badges", 175)
|
||
|
||
|
||
def makeExtension(**kwargs):
|
||
return InlineBadgeExtension(**kwargs)
|