Skip to content

Commit

Permalink
feat: Support ANSI code blocks
Browse files Browse the repository at this point in the history
Issue #11: #11
  • Loading branch information
pawamoy committed Feb 1, 2023
1 parent 162800d commit 39719c5
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 5 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ and this HTML is injected in place of the code block.

With `pip`:
```bash
pip install markdown-exec
pip install markdown-exec[ansi]
```

The `ansi` extra provides the necessary bits (`pygments-ansi-color` and a CSS file)
to render ANSI colors in HTML code blocks. The CSS file is automatically added
to MkDocs' `extra_css` when Markdown Exec is activated via `plugins` (see below).

## Configuration

This extension relies on the
Expand Down
12 changes: 11 additions & 1 deletion docs/gallery.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,17 @@ but also allows for less verbose source to generate the SVG snippets.

## Terminal output with colors

We use Rich again to render the output of a command in a terminal, with colors.
If you installed Markdown Exec with the `ansi` extra (`pip install markdown-exec[ansi]`),
the ANSI colors in the output of shell commands will be translated to HTML/CSS,
allowing to render them naturally in your documentation pages.
For this to happen, use the
[`result="ansi"` option](http://localhost:8000/markdown-exec/usage/#wrap-result-in-a-code-block).

```bash exec="true" source="tabbed-right" title="ANSI terminal output" result="ansi"
--8<-- "gallery/ansi.sh"
```

As an alternative, we can use Rich again to render the output of a command in a terminal, with colors.
This example is taken directly from the documentation of the [Griffe](https:/mkdocstrings/griffe) project.

```python exec="true" html="true" source="tabbed-right" title="Rich terminal output"
Expand Down
15 changes: 15 additions & 0 deletions docs/snippets/gallery/ansi.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
# credits to https:/42picky/42picky.github.io
text="xYz" # Some test text
echo -e "\n 40m 41m 42m 43m 44m 45m 46m 47m"
for FGs in ' m' ' 1m' ' 30m' '1;30m' ' 31m' '1;31m' ' 32m' \
'1;32m' ' 33m' '1;33m' ' 34m' '1;34m' ' 35m' '1;35m' \
' 36m' '1;36m' ' 37m' '1;37m'; do
FG=${FGs// /}
echo -en " $FGs \033[$FG ${text} "
for BG in 40m 41m 42m 43m 44m 45m 46m 47m; do
echo -en "$EINS \033[$FG\033[${BG} ${text} \033[0m"
done
echo
done
echo
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ dependencies = [
"pymdown-extensions>=9",
]

[project.optional-dependencies]
ansi = ["pygments-ansi-color"]

[project.urls]
Homepage = "https://pawamoy.github.io/markdown-exec"
Documentation = "https://pawamoy.github.io/markdown-exec"
Expand Down
4 changes: 2 additions & 2 deletions scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ if [ -n "${PYTHON_VERSIONS}" ]; then
for python_version in ${PYTHON_VERSIONS}; do
if pdm use -f "python${python_version}" &>/dev/null; then
echo "> Using Python ${python_version} interpreter"
pdm install
pdm install -G ansi
else
echo "> pdm use -f python${python_version}: Python interpreter not available?" >&2
fi
done
else
pdm install
pdm install -G ansi
fi
266 changes: 266 additions & 0 deletions src/markdown_exec/ansi.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
/*
Inspired by https://spec.draculatheme.com/ specification, they should work
decently with both dark and light themes.
*/
:root {
--ansi-red: #ff5555;
--ansi-green: #50fa7b;
--ansi-blue: #265285;
--ansi-yellow: #ffb86c;
--ansi-magenta: #bd93f9;
--ansi-cyan: #8be9fd;
--ansi-black: #282a36;
--ansi-white: #f8f8f2;
}

.-Color-Green,
.-Color-Faint-Green,
.-Color-Bold-Green {
color: var(--ansi-green);
}

.-Color-Red,
.-Color-Faint-Red,
.-Color-Bold-Red {
color: var(--ansi-red);
}

.-Color-Yellow,
.-Color-Faint-Yellow,
.-Color-Bold-Yellow {
color: var(--ansi-yellow);
}

.-Color-Blue,
.-Color-Faint-Blue,
.-Color-Bold-Blue {
color: var(--ansi-blue);
}

.-Color-Magenta,
.-Color-Faint-Magenta,
.-Color-Bold-Magenta {
color: var(--ansi-magenta);
}

.-Color-Cyan,
.-Color-Faint-Cyan,
.-Color-Bold-Cyan {
color: var(--ansi-cyan);
}

.-Color-White,
.-Color-Faint-White,
.-Color-Bold-White {
color: var(--ansi-white);
}

.-Color-Black,
.-Color-Faint-Black,
.-Color-Bold-Black {
color: var(--ansi-black);
}

.-Color-Faint {
opacity: 0.5;
}

.-Color-Bold {
font-weight: bold;
}

.-Color-BGBlack,
.-Color-Black-BGBlack,
.-Color-Blue-BGBlack,
.-Color-Bold-BGBlack,
.-Color-Bold-Black-BGBlack,
.-Color-Bold-Green-BGBlack,
.-Color-Bold-Cyan-BGBlack,
.-Color-Bold-Blue-BGBlack,
.-Color-Bold-Magenta-BGBlack,
.-Color-Bold-Red-BGBlack,
.-Color-Bold-White-BGBlack,
.-Color-Bold-Yellow-BGBlack,
.-Color-Cyan-BGBlack,
.-Color-Green-BGBlack,
.-Color-Magenta-BGBlack,
.-Color-Red-BGBlack,
.-Color-White-BGBlack,
.-Color-Yellow-BGBlack {
background-color: var(--ansi-black);
}

.-Color-BGRed,
.-Color-Black-BGRed,
.-Color-Blue-BGRed,
.-Color-Bold-BGRed,
.-Color-Bold-Black-BGRed,
.-Color-Bold-Green-BGRed,
.-Color-Bold-Cyan-BGRed,
.-Color-Bold-Blue-BGRed,
.-Color-Bold-Magenta-BGRed,
.-Color-Bold-Red-BGRed,
.-Color-Bold-White-BGRed,
.-Color-Bold-Yellow-BGRed,
.-Color-Cyan-BGRed,
.-Color-Green-BGRed,
.-Color-Magenta-BGRed,
.-Color-Red-BGRed,
.-Color-White-BGRed,
.-Color-Yellow-BGRed {
background-color: var(--ansi-red);
}

.-Color-BGGreen,
.-Color-Black-BGGreen,
.-Color-Blue-BGGreen,
.-Color-Bold-BGGreen,
.-Color-Bold-Black-BGGreen,
.-Color-Bold-Green-BGGreen,
.-Color-Bold-Cyan-BGGreen,
.-Color-Bold-Blue-BGGreen,
.-Color-Bold-Magenta-BGGreen,
.-Color-Bold-Red-BGGreen,
.-Color-Bold-White-BGGreen,
.-Color-Bold-Yellow-BGGreen,
.-Color-Cyan-BGGreen,
.-Color-Green-BGGreen,
.-Color-Magenta-BGGreen,
.-Color-Red-BGGreen,
.-Color-White-BGGreen,
.-Color-Yellow-BGGreen {
background-color: var(--ansi-green);
}

.-Color-BGYellow,
.-Color-Black-BGYellow,
.-Color-Blue-BGYellow,
.-Color-Bold-BGYellow,
.-Color-Bold-Black-BGYellow,
.-Color-Bold-Green-BGYellow,
.-Color-Bold-Cyan-BGYellow,
.-Color-Bold-Blue-BGYellow,
.-Color-Bold-Magenta-BGYellow,
.-Color-Bold-Red-BGYellow,
.-Color-Bold-White-BGYellow,
.-Color-Bold-Yellow-BGYellow,
.-Color-Cyan-BGYellow,
.-Color-Green-BGYellow,
.-Color-Magenta-BGYellow,
.-Color-Red-BGYellow,
.-Color-White-BGYellow,
.-Color-Yellow-BGYellow {
background-color: var(--ansi-yellow);
}

.-Color-BGBlue,
.-Color-Black-BGBlue,
.-Color-Blue-BGBlue,
.-Color-Bold-BGBlue,
.-Color-Bold-Black-BGBlue,
.-Color-Bold-Green-BGBlue,
.-Color-Bold-Cyan-BGBlue,
.-Color-Bold-Blue-BGBlue,
.-Color-Bold-Magenta-BGBlue,
.-Color-Bold-Red-BGBlue,
.-Color-Bold-White-BGBlue,
.-Color-Bold-Yellow-BGBlue,
.-Color-Cyan-BGBlue,
.-Color-Green-BGBlue,
.-Color-Magenta-BGBlue,
.-Color-Red-BGBlue,
.-Color-White-BGBlue,
.-Color-Yellow-BGBlue {
background-color: var(--ansi-blue);
}

.-Color-BGMagenta,
.-Color-Black-BGMagenta,
.-Color-Blue-BGMagenta,
.-Color-Bold-BGMagenta,
.-Color-Bold-Black-BGMagenta,
.-Color-Bold-Green-BGMagenta,
.-Color-Bold-Cyan-BGMagenta,
.-Color-Bold-Blue-BGMagenta,
.-Color-Bold-Magenta-BGMagenta,
.-Color-Bold-Red-BGMagenta,
.-Color-Bold-White-BGMagenta,
.-Color-Bold-Yellow-BGMagenta,
.-Color-Cyan-BGMagenta,
.-Color-Green-BGMagenta,
.-Color-Magenta-BGMagenta,
.-Color-Red-BGMagenta,
.-Color-White-BGMagenta,
.-Color-Yellow-BGMagenta {
background-color: var(--ansi-magenta);
}

.-Color-BGCyan,
.-Color-Black-BGCyan,
.-Color-Blue-BGCyan,
.-Color-Bold-BGCyan,
.-Color-Bold-Black-BGCyan,
.-Color-Bold-Green-BGCyan,
.-Color-Bold-Cyan-BGCyan,
.-Color-Bold-Blue-BGCyan,
.-Color-Bold-Magenta-BGCyan,
.-Color-Bold-Red-BGCyan,
.-Color-Bold-White-BGCyan,
.-Color-Bold-Yellow-BGCyan,
.-Color-Cyan-BGCyan,
.-Color-Green-BGCyan,
.-Color-Magenta-BGCyan,
.-Color-Red-BGCyan,
.-Color-White-BGCyan,
.-Color-Yellow-BGCyan {
background-color: var(--ansi-cyan);
}

.-Color-BGWhite,
.-Color-Black-BGWhite,
.-Color-Blue-BGWhite,
.-Color-Bold-BGWhite,
.-Color-Bold-Black-BGWhite,
.-Color-Bold-Green-BGWhite,
.-Color-Bold-Cyan-BGWhite,
.-Color-Bold-Blue-BGWhite,
.-Color-Bold-Magenta-BGWhite,
.-Color-Bold-Red-BGWhite,
.-Color-Bold-White-BGWhite,
.-Color-Bold-Yellow-BGWhite,
.-Color-Cyan-BGWhite,
.-Color-Green-BGWhite,
.-Color-Magenta-BGWhite,
.-Color-Red-BGWhite,
.-Color-White-BGWhite,
.-Color-Yellow-BGWhite {
background-color: var(--ansi-white);
}

.-Color-Black,
.-Color-Bold-Black,
.-Color-Black-BGBlack,
.-Color-Bold-Black-BGBlack,
.-Color-Black-BGGreen,
.-Color-Red-BGRed,
.-Color-Bold-Red-BGRed,
.-Color-Bold-Blue-BGBlue,
.-Color-Blue-BGBlue {
text-shadow: 0 0 1px var(--ansi-white);
}

.-Color-Bold-Cyan-BGCyan,
.-Color-Bold-Magenta-BGMagenta,
.-Color-Bold-White,
.-Color-Bold-Yellow-BGYellow,
.-Color-Bold-Green-BGGreen,
.-Color-Cyan-BGCyan,
.-Color-Cyan-BGGreen,
.-Color-Green-BGCyan,
.-Color-Green-BGGreen,
.-Color-Magenta-BGMagenta,
.-Color-White,
.-Color-White-BGWhite,
.-Color-Yellow-BGYellow {
text-shadow: 0 0 1px var(--ansi-black);
}
25 changes: 24 additions & 1 deletion src/markdown_exec/mkdocs_plugin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
"""This module contains an optional plugin for MkDocs."""

from __future__ import annotations

import logging
import os
from pathlib import Path
from typing import TYPE_CHECKING

from mkdocs.config import Config, config_options
from mkdocs.plugins import BasePlugin
from mkdocs.utils import write_file

from markdown_exec import formatter, formatters, validator
from markdown_exec.logger import patch_loggers

if TYPE_CHECKING:
from jinja2 import Environment
from mkdocs.structure.files import Files

try:
__import__("pygments_ansi_color")
except ImportError:
ansi_ok = False
else:
ansi_ok = True


class _LoggerAdapter(logging.LoggerAdapter):
def __init__(self, prefix, logger):
Expand All @@ -33,7 +50,6 @@ class MarkdownExecPlugin(BasePlugin):

def on_config(self, config: Config, **kwargs) -> Config: # noqa: D102
self.languages = self.config["languages"]

mdx_configs = config.setdefault("mdx_configs", {})
superfences = mdx_configs.setdefault("pymdownx.superfences", {})
custom_fences = superfences.setdefault("custom_fences", [])
Expand All @@ -47,3 +63,10 @@ def on_config(self, config: Config, **kwargs) -> Config: # noqa: D102
}
)
return config

def on_env(self, env: Environment, *, config: Config, files: Files) -> Environment | None: # noqa: D102
css_filename = "assets/_markdown_exec_ansi.css"
css_content = Path(__file__).parent.joinpath("ansi.css").read_text()
write_file(css_content.encode("utf-8"), os.path.join(config["site_dir"], css_filename))
config["extra_css"].insert(0, css_filename)
return env

0 comments on commit 39719c5

Please sign in to comment.