Source code for click_extra.mkdocs
# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""MkDocs plugin for ANSI color rendering in code blocks.
Patches `pymdownx.highlight <https://facelessuser.github.io/pymdown-extensions/
extensions/highlight/>`_'s formatter classes so that ``Token.Ansi.*`` tokens produced by
Click Extra's :doc:`Pygments lexers <pygments>` are decomposed into individual CSS classes
and styled with the correct colors.
When `mkdocs-click <https://pypi.org/project/mkdocs-click/>`_ is installed, the plugin
also patches its code-block generators to use the ``ansi-output`` lexer instead of plain
``text``, so that CLI help text with ANSI escape codes renders with colors.
.. note::
This is the MkDocs counterpart of the Sphinx integration in
:mod:`click_extra.sphinx`, which achieves the same result by replacing
``sphinx.highlighting.PygmentsBridge.html_formatter``.
"""
from __future__ import annotations
from collections.abc import Iterator
from functools import wraps
try:
from mkdocs.plugins import BasePlugin
except ImportError:
raise ImportError(
"You need to install click_extra[mkdocs] dependency group to use this module."
)
import pymdownx.highlight
from .pygments import AnsiHtmlFormatter
ANSI_OUTPUT_FENCE = "```ansi-output"
"""Fenced code-block opening that triggers the ANSI-aware Pygments lexer."""
TEXT_FENCE = "```text"
"""Fenced code-block opening used by ``mkdocs-click`` for CLI help output."""
TYPE_CHECKING = False
if TYPE_CHECKING:
from mkdocs.config.defaults import MkDocsConfig
def _ansi_lines(gen: Iterator[str]) -> Iterator[str]:
"""Replace ``TEXT_FENCE`` with ``ANSI_OUTPUT_FENCE`` in a line iterator."""
for line in gen:
yield ANSI_OUTPUT_FENCE if line == TEXT_FENCE else line
def _patch_mkdocs_click() -> None:
"""Patch ``mkdocs-click`` code blocks to use the ``ansi-output`` lexer.
Wraps ``_make_usage`` and ``_make_plain_options`` so that their fenced
code blocks use the ANSI-aware lexer instead of plain ``text``. The patch
is idempotent: calling it twice has no additional effect.
"""
try:
from mkdocs_click import _docs
except ImportError:
return
if getattr(_docs, "_click_extra_patched", False):
return
orig_usage = _docs._make_usage
orig_plain = _docs._make_plain_options
@wraps(orig_usage)
def _ansi_usage(*args, **kwargs):
return _ansi_lines(orig_usage(*args, **kwargs))
@wraps(orig_plain)
def _ansi_plain_options(*args, **kwargs):
return _ansi_lines(orig_plain(*args, **kwargs))
_docs._make_usage = _ansi_usage
_docs._make_plain_options = _ansi_plain_options
_docs._click_extra_patched = True # type: ignore[attr-defined]
[docs]
class AnsiColorPlugin(BasePlugin):
"""MkDocs plugin that adds ANSI color support to Pygments code blocks.
Monkey-patches ``pymdownx.highlight``'s block and inline formatter classes to
inherit from :class:`~click_extra.pygments.AnsiHtmlFormatter`. This gives every code
block in the MkDocs site full ANSI color rendering: compound tokens like
``Token.Ansi.Bold.Cyan`` are decomposed into individual CSS classes, and the
stylesheet includes rules for the 256-color indexed palette and all SGR text
attributes.
When ``mkdocs-click`` is installed, its code-block generators are also patched to use
the ``ansi-output`` lexer so that CLI help text renders with colors.
"""
[docs]
def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
"""Patch pymdownx.highlight formatters before page processing begins."""
if not issubclass(pymdownx.highlight.BlockHtmlFormatter, AnsiHtmlFormatter):
pymdownx.highlight.BlockHtmlFormatter = type(
"AnsiBlockHtmlFormatter",
(AnsiHtmlFormatter, pymdownx.highlight.BlockHtmlFormatter),
{},
)
if not issubclass(pymdownx.highlight.InlineHtmlFormatter, AnsiHtmlFormatter):
pymdownx.highlight.InlineHtmlFormatter = type(
"AnsiInlineHtmlFormatter",
(AnsiHtmlFormatter, pymdownx.highlight.InlineHtmlFormatter),
{},
)
_patch_mkdocs_click()
return config