Skip to content

Commit 39719c5

Browse files
committed
feat: Support ANSI code blocks
Issue #11: #11
1 parent 162800d commit 39719c5

7 files changed

Lines changed: 326 additions & 5 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@ and this HTML is injected in place of the code block.
1515

1616
With `pip`:
1717
```bash
18-
pip install markdown-exec
18+
pip install markdown-exec[ansi]
1919
```
2020

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

2327
This extension relies on the

docs/gallery.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,17 @@ but also allows for less verbose source to generate the SVG snippets.
7070

7171
## Terminal output with colors
7272

73-
We use Rich again to render the output of a command in a terminal, with colors.
73+
If you installed Markdown Exec with the `ansi` extra (`pip install markdown-exec[ansi]`),
74+
the ANSI colors in the output of shell commands will be translated to HTML/CSS,
75+
allowing to render them naturally in your documentation pages.
76+
For this to happen, use the
77+
[`result="ansi"` option](http://localhost:8000/markdown-exec/usage/#wrap-result-in-a-code-block).
78+
79+
```bash exec="true" source="tabbed-right" title="ANSI terminal output" result="ansi"
80+
--8<-- "gallery/ansi.sh"
81+
```
82+
83+
As an alternative, we can use Rich again to render the output of a command in a terminal, with colors.
7484
This example is taken directly from the documentation of the [Griffe](https://github.com/mkdocstrings/griffe) project.
7585

7686
```python exec="true" html="true" source="tabbed-right" title="Rich terminal output"

docs/snippets/gallery/ansi.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
# credits to https://github.com/42picky/42picky.github.io
3+
text="xYz" # Some test text
4+
echo -e "\n 40m 41m 42m 43m 44m 45m 46m 47m"
5+
for FGs in ' m' ' 1m' ' 30m' '1;30m' ' 31m' '1;31m' ' 32m' \
6+
'1;32m' ' 33m' '1;33m' ' 34m' '1;34m' ' 35m' '1;35m' \
7+
' 36m' '1;36m' ' 37m' '1;37m'; do
8+
FG=${FGs// /}
9+
echo -en " $FGs \033[$FG ${text} "
10+
for BG in 40m 41m 42m 43m 44m 45m 46m 47m; do
11+
echo -en "$EINS \033[$FG\033[${BG} ${text} \033[0m"
12+
done
13+
echo
14+
done
15+
echo

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ dependencies = [
3232
"pymdown-extensions>=9",
3333
]
3434

35+
[project.optional-dependencies]
36+
ansi = ["pygments-ansi-color"]
37+
3538
[project.urls]
3639
Homepage = "https://pawamoy.github.io/markdown-exec"
3740
Documentation = "https://pawamoy.github.io/markdown-exec"

scripts/setup.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ if [ -n "${PYTHON_VERSIONS}" ]; then
2828
for python_version in ${PYTHON_VERSIONS}; do
2929
if pdm use -f "python${python_version}" &>/dev/null; then
3030
echo "> Using Python ${python_version} interpreter"
31-
pdm install
31+
pdm install -G ansi
3232
else
3333
echo "> pdm use -f python${python_version}: Python interpreter not available?" >&2
3434
fi
3535
done
3636
else
37-
pdm install
37+
pdm install -G ansi
3838
fi

src/markdown_exec/ansi.css

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
Inspired by https://spec.draculatheme.com/ specification, they should work
3+
decently with both dark and light themes.
4+
*/
5+
:root {
6+
--ansi-red: #ff5555;
7+
--ansi-green: #50fa7b;
8+
--ansi-blue: #265285;
9+
--ansi-yellow: #ffb86c;
10+
--ansi-magenta: #bd93f9;
11+
--ansi-cyan: #8be9fd;
12+
--ansi-black: #282a36;
13+
--ansi-white: #f8f8f2;
14+
}
15+
16+
.-Color-Green,
17+
.-Color-Faint-Green,
18+
.-Color-Bold-Green {
19+
color: var(--ansi-green);
20+
}
21+
22+
.-Color-Red,
23+
.-Color-Faint-Red,
24+
.-Color-Bold-Red {
25+
color: var(--ansi-red);
26+
}
27+
28+
.-Color-Yellow,
29+
.-Color-Faint-Yellow,
30+
.-Color-Bold-Yellow {
31+
color: var(--ansi-yellow);
32+
}
33+
34+
.-Color-Blue,
35+
.-Color-Faint-Blue,
36+
.-Color-Bold-Blue {
37+
color: var(--ansi-blue);
38+
}
39+
40+
.-Color-Magenta,
41+
.-Color-Faint-Magenta,
42+
.-Color-Bold-Magenta {
43+
color: var(--ansi-magenta);
44+
}
45+
46+
.-Color-Cyan,
47+
.-Color-Faint-Cyan,
48+
.-Color-Bold-Cyan {
49+
color: var(--ansi-cyan);
50+
}
51+
52+
.-Color-White,
53+
.-Color-Faint-White,
54+
.-Color-Bold-White {
55+
color: var(--ansi-white);
56+
}
57+
58+
.-Color-Black,
59+
.-Color-Faint-Black,
60+
.-Color-Bold-Black {
61+
color: var(--ansi-black);
62+
}
63+
64+
.-Color-Faint {
65+
opacity: 0.5;
66+
}
67+
68+
.-Color-Bold {
69+
font-weight: bold;
70+
}
71+
72+
.-Color-BGBlack,
73+
.-Color-Black-BGBlack,
74+
.-Color-Blue-BGBlack,
75+
.-Color-Bold-BGBlack,
76+
.-Color-Bold-Black-BGBlack,
77+
.-Color-Bold-Green-BGBlack,
78+
.-Color-Bold-Cyan-BGBlack,
79+
.-Color-Bold-Blue-BGBlack,
80+
.-Color-Bold-Magenta-BGBlack,
81+
.-Color-Bold-Red-BGBlack,
82+
.-Color-Bold-White-BGBlack,
83+
.-Color-Bold-Yellow-BGBlack,
84+
.-Color-Cyan-BGBlack,
85+
.-Color-Green-BGBlack,
86+
.-Color-Magenta-BGBlack,
87+
.-Color-Red-BGBlack,
88+
.-Color-White-BGBlack,
89+
.-Color-Yellow-BGBlack {
90+
background-color: var(--ansi-black);
91+
}
92+
93+
.-Color-BGRed,
94+
.-Color-Black-BGRed,
95+
.-Color-Blue-BGRed,
96+
.-Color-Bold-BGRed,
97+
.-Color-Bold-Black-BGRed,
98+
.-Color-Bold-Green-BGRed,
99+
.-Color-Bold-Cyan-BGRed,
100+
.-Color-Bold-Blue-BGRed,
101+
.-Color-Bold-Magenta-BGRed,
102+
.-Color-Bold-Red-BGRed,
103+
.-Color-Bold-White-BGRed,
104+
.-Color-Bold-Yellow-BGRed,
105+
.-Color-Cyan-BGRed,
106+
.-Color-Green-BGRed,
107+
.-Color-Magenta-BGRed,
108+
.-Color-Red-BGRed,
109+
.-Color-White-BGRed,
110+
.-Color-Yellow-BGRed {
111+
background-color: var(--ansi-red);
112+
}
113+
114+
.-Color-BGGreen,
115+
.-Color-Black-BGGreen,
116+
.-Color-Blue-BGGreen,
117+
.-Color-Bold-BGGreen,
118+
.-Color-Bold-Black-BGGreen,
119+
.-Color-Bold-Green-BGGreen,
120+
.-Color-Bold-Cyan-BGGreen,
121+
.-Color-Bold-Blue-BGGreen,
122+
.-Color-Bold-Magenta-BGGreen,
123+
.-Color-Bold-Red-BGGreen,
124+
.-Color-Bold-White-BGGreen,
125+
.-Color-Bold-Yellow-BGGreen,
126+
.-Color-Cyan-BGGreen,
127+
.-Color-Green-BGGreen,
128+
.-Color-Magenta-BGGreen,
129+
.-Color-Red-BGGreen,
130+
.-Color-White-BGGreen,
131+
.-Color-Yellow-BGGreen {
132+
background-color: var(--ansi-green);
133+
}
134+
135+
.-Color-BGYellow,
136+
.-Color-Black-BGYellow,
137+
.-Color-Blue-BGYellow,
138+
.-Color-Bold-BGYellow,
139+
.-Color-Bold-Black-BGYellow,
140+
.-Color-Bold-Green-BGYellow,
141+
.-Color-Bold-Cyan-BGYellow,
142+
.-Color-Bold-Blue-BGYellow,
143+
.-Color-Bold-Magenta-BGYellow,
144+
.-Color-Bold-Red-BGYellow,
145+
.-Color-Bold-White-BGYellow,
146+
.-Color-Bold-Yellow-BGYellow,
147+
.-Color-Cyan-BGYellow,
148+
.-Color-Green-BGYellow,
149+
.-Color-Magenta-BGYellow,
150+
.-Color-Red-BGYellow,
151+
.-Color-White-BGYellow,
152+
.-Color-Yellow-BGYellow {
153+
background-color: var(--ansi-yellow);
154+
}
155+
156+
.-Color-BGBlue,
157+
.-Color-Black-BGBlue,
158+
.-Color-Blue-BGBlue,
159+
.-Color-Bold-BGBlue,
160+
.-Color-Bold-Black-BGBlue,
161+
.-Color-Bold-Green-BGBlue,
162+
.-Color-Bold-Cyan-BGBlue,
163+
.-Color-Bold-Blue-BGBlue,
164+
.-Color-Bold-Magenta-BGBlue,
165+
.-Color-Bold-Red-BGBlue,
166+
.-Color-Bold-White-BGBlue,
167+
.-Color-Bold-Yellow-BGBlue,
168+
.-Color-Cyan-BGBlue,
169+
.-Color-Green-BGBlue,
170+
.-Color-Magenta-BGBlue,
171+
.-Color-Red-BGBlue,
172+
.-Color-White-BGBlue,
173+
.-Color-Yellow-BGBlue {
174+
background-color: var(--ansi-blue);
175+
}
176+
177+
.-Color-BGMagenta,
178+
.-Color-Black-BGMagenta,
179+
.-Color-Blue-BGMagenta,
180+
.-Color-Bold-BGMagenta,
181+
.-Color-Bold-Black-BGMagenta,
182+
.-Color-Bold-Green-BGMagenta,
183+
.-Color-Bold-Cyan-BGMagenta,
184+
.-Color-Bold-Blue-BGMagenta,
185+
.-Color-Bold-Magenta-BGMagenta,
186+
.-Color-Bold-Red-BGMagenta,
187+
.-Color-Bold-White-BGMagenta,
188+
.-Color-Bold-Yellow-BGMagenta,
189+
.-Color-Cyan-BGMagenta,
190+
.-Color-Green-BGMagenta,
191+
.-Color-Magenta-BGMagenta,
192+
.-Color-Red-BGMagenta,
193+
.-Color-White-BGMagenta,
194+
.-Color-Yellow-BGMagenta {
195+
background-color: var(--ansi-magenta);
196+
}
197+
198+
.-Color-BGCyan,
199+
.-Color-Black-BGCyan,
200+
.-Color-Blue-BGCyan,
201+
.-Color-Bold-BGCyan,
202+
.-Color-Bold-Black-BGCyan,
203+
.-Color-Bold-Green-BGCyan,
204+
.-Color-Bold-Cyan-BGCyan,
205+
.-Color-Bold-Blue-BGCyan,
206+
.-Color-Bold-Magenta-BGCyan,
207+
.-Color-Bold-Red-BGCyan,
208+
.-Color-Bold-White-BGCyan,
209+
.-Color-Bold-Yellow-BGCyan,
210+
.-Color-Cyan-BGCyan,
211+
.-Color-Green-BGCyan,
212+
.-Color-Magenta-BGCyan,
213+
.-Color-Red-BGCyan,
214+
.-Color-White-BGCyan,
215+
.-Color-Yellow-BGCyan {
216+
background-color: var(--ansi-cyan);
217+
}
218+
219+
.-Color-BGWhite,
220+
.-Color-Black-BGWhite,
221+
.-Color-Blue-BGWhite,
222+
.-Color-Bold-BGWhite,
223+
.-Color-Bold-Black-BGWhite,
224+
.-Color-Bold-Green-BGWhite,
225+
.-Color-Bold-Cyan-BGWhite,
226+
.-Color-Bold-Blue-BGWhite,
227+
.-Color-Bold-Magenta-BGWhite,
228+
.-Color-Bold-Red-BGWhite,
229+
.-Color-Bold-White-BGWhite,
230+
.-Color-Bold-Yellow-BGWhite,
231+
.-Color-Cyan-BGWhite,
232+
.-Color-Green-BGWhite,
233+
.-Color-Magenta-BGWhite,
234+
.-Color-Red-BGWhite,
235+
.-Color-White-BGWhite,
236+
.-Color-Yellow-BGWhite {
237+
background-color: var(--ansi-white);
238+
}
239+
240+
.-Color-Black,
241+
.-Color-Bold-Black,
242+
.-Color-Black-BGBlack,
243+
.-Color-Bold-Black-BGBlack,
244+
.-Color-Black-BGGreen,
245+
.-Color-Red-BGRed,
246+
.-Color-Bold-Red-BGRed,
247+
.-Color-Bold-Blue-BGBlue,
248+
.-Color-Blue-BGBlue {
249+
text-shadow: 0 0 1px var(--ansi-white);
250+
}
251+
252+
.-Color-Bold-Cyan-BGCyan,
253+
.-Color-Bold-Magenta-BGMagenta,
254+
.-Color-Bold-White,
255+
.-Color-Bold-Yellow-BGYellow,
256+
.-Color-Bold-Green-BGGreen,
257+
.-Color-Cyan-BGCyan,
258+
.-Color-Cyan-BGGreen,
259+
.-Color-Green-BGCyan,
260+
.-Color-Green-BGGreen,
261+
.-Color-Magenta-BGMagenta,
262+
.-Color-White,
263+
.-Color-White-BGWhite,
264+
.-Color-Yellow-BGYellow {
265+
text-shadow: 0 0 1px var(--ansi-black);
266+
}

src/markdown_exec/mkdocs_plugin.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
"""This module contains an optional plugin for MkDocs."""
22

3+
from __future__ import annotations
4+
35
import logging
6+
import os
7+
from pathlib import Path
8+
from typing import TYPE_CHECKING
49

510
from mkdocs.config import Config, config_options
611
from mkdocs.plugins import BasePlugin
12+
from mkdocs.utils import write_file
713

814
from markdown_exec import formatter, formatters, validator
915
from markdown_exec.logger import patch_loggers
1016

17+
if TYPE_CHECKING:
18+
from jinja2 import Environment
19+
from mkdocs.structure.files import Files
20+
21+
try:
22+
__import__("pygments_ansi_color")
23+
except ImportError:
24+
ansi_ok = False
25+
else:
26+
ansi_ok = True
27+
1128

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

3451
def on_config(self, config: Config, **kwargs) -> Config: # noqa: D102
3552
self.languages = self.config["languages"]
36-
3753
mdx_configs = config.setdefault("mdx_configs", {})
3854
superfences = mdx_configs.setdefault("pymdownx.superfences", {})
3955
custom_fences = superfences.setdefault("custom_fences", [])
@@ -47,3 +63,10 @@ def on_config(self, config: Config, **kwargs) -> Config: # noqa: D102
4763
}
4864
)
4965
return config
66+
67+
def on_env(self, env: Environment, *, config: Config, files: Files) -> Environment | None: # noqa: D102
68+
css_filename = "assets/_markdown_exec_ansi.css"
69+
css_content = Path(__file__).parent.joinpath("ansi.css").read_text()
70+
write_file(css_content.encode("utf-8"), os.path.join(config["site_dir"], css_filename))
71+
config["extra_css"].insert(0, css_filename)
72+
return env

0 commit comments

Comments
 (0)