# 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"", 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'', f'{icon}', *([f'{text}'] if text else []), '', ]) class InlineBadgeExtension(Extension): def extendMarkdown(self, md): md.preprocessors.register(BadgePreprocessor(md), "inline_badges", 175) def makeExtension(**kwargs): return InlineBadgeExtension(**kwargs)