Skip to content

Commit cd2c343

Browse files
feat: ✨ beautify CLI output in docs (#31)
# Description This strips newlines, makes the output more narrow, enable overflow instead of wrapping, and remaps some colors. See motivation and an example of how these changes look in seedcase-project/check-datapackage#342 Needs thorough review. ## Checklist - [x] Ran `just run-all` --------- Co-authored-by: Luke W. Johnston <lwjohnst86@users.noreply.github.com>
1 parent 19b3568 commit cd2c343

3 files changed

Lines changed: 103 additions & 0 deletions

File tree

_quarto.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ quartodoc:
7070
- "parse_source"
7171
- "read_properties"
7272
- "write_properties"
73+
- title: "Documentation helpers"
74+
desc: "Functionality that helps with creating our documentation."
75+
contents:
76+
- "format_output_for_docs"
7377

7478
metadata-files:
7579
- docs/reference/_sidebar.yml

src/seedcase_soil/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
run_without_tracebacks,
88
setup_cli,
99
)
10+
from .docs import format_output_for_docs
1011
from .example_datapackage import Example
1112
from .functionals import flat_fmap, fmap, keep, pairwise_fmap
1213
from .parse_source import Address, parse_source
@@ -18,6 +19,7 @@
1819
"print_if_verbose",
1920
"run_without_tracebacks",
2021
"setup_cli",
22+
"format_output_for_docs",
2123
"Example",
2224
"fmap",
2325
"pairwise_fmap",

src/seedcase_soil/docs.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""Helpers for rendering command output in documentation."""
2+
3+
import os
4+
import subprocess # nosec B404
5+
from collections.abc import Sequence
6+
from io import StringIO
7+
8+
from rich.ansi import AnsiDecoder
9+
from rich.console import Console
10+
11+
12+
def format_output_for_docs(
13+
command: Sequence[str],
14+
*,
15+
width: int = 60,
16+
) -> None:
17+
"""Run a command and display compact, colored output in docs.
18+
19+
This strips newlines, makes the output more narrow, enable overflow
20+
instead of wrapping, and remaps some colors. Currently everything
21+
except the output width is hard-coded to a certain style that emulates
22+
the look of the output when running in the terminal, but this could
23+
be parametrized later if the need arises.
24+
25+
Args:
26+
command: The command to run, as a list of strings.
27+
width: The terminal width used when rendering command output.
28+
29+
Examples:
30+
To run the command `cdp check --strict` and format the output:
31+
```python
32+
format_output_for_docs(["cdp", "check", "--strict"])
33+
```
34+
"""
35+
from IPython.display import display
36+
37+
full_env = os.environ.copy()
38+
full_env.update({"COLUMNS": str(width), "FORCE_COLOR": "1"})
39+
40+
result = subprocess.run( # nosec B603
41+
command,
42+
env=full_env,
43+
stdout=subprocess.PIPE,
44+
stderr=subprocess.STDOUT,
45+
text=True,
46+
check=False,
47+
)
48+
display({"text/html": _html_output(result.stdout)}, raw=True) # type: ignore[no-untyped-call]
49+
50+
51+
def _html_output(output: str) -> str:
52+
pre_style = (
53+
"overflow-x: auto; line-height: normal; "
54+
"background-color: #1A1B26; color: #C0CAF5; "
55+
"border-radius: 0.5rem; padding: 0.5rem; "
56+
"font-family: Menlo, 'DejaVu Sans Mono', consolas, 'Courier New', monospace;"
57+
)
58+
code_style = (
59+
"white-space: pre; overflow-wrap: normal; word-break: normal; "
60+
"background: transparent; color: inherit;"
61+
)
62+
return (
63+
f'<pre style="{pre_style}">'
64+
f'<code style="{code_style}">{_ansi_to_html(output)}</code></pre>'
65+
)
66+
67+
68+
def _ansi_to_html(output: str) -> str:
69+
console = Console(record=True, file=StringIO(), force_jupyter=False, width=200)
70+
lines = list(AnsiDecoder().decode(output.rstrip()))
71+
console.print(*lines, sep="\n", soft_wrap=True)
72+
html = console.export_html(inline_styles=True, code_format="{code}").rstrip()
73+
bold_style = "color: inherit; text-decoration-color: inherit; font-weight: bold"
74+
underline_style = "color: inherit; text-decoration-color: inherit"
75+
return (
76+
html.replace(
77+
'<span style="font-weight: bold">',
78+
f'<span style="{bold_style}">',
79+
)
80+
.replace(
81+
'<span style="text-decoration: underline">',
82+
'<span style="color: inherit; text-decoration-color: inherit; '
83+
'text-decoration: underline">',
84+
)
85+
.replace(
86+
"color: #008080; text-decoration-color: #008080; "
87+
"text-decoration: underline",
88+
f"{underline_style}; text-decoration: underline",
89+
)
90+
.replace("#003B4F", "inherit")
91+
.replace("#800000", "#F7768E")
92+
.replace("#000080", "#7AA2F7")
93+
.replace("#008000", "#9ECE6A")
94+
.replace("#008080", "#7DCFFF")
95+
.replace("#808080", "#8087A5")
96+
.replace("#808000", "#E0AF68")
97+
)

0 commit comments

Comments
 (0)