2dbae7a went red on lint, and it was never ruff check -- that passed. It was
ruff format --check, on two files from the last few commits: a nested def with no blank
line above it in the session, and a sum() in test_logo that fits on one line once
joined. Both pure layout; neither touches a hand-aligned table, and ruff check stays
clean after, which is the thing this repository has been bitten by before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
117 lines
5.2 KiB
Python
117 lines
5.2 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""The splash art's colour rewriting: brightness said as a colour, never as bold.
|
|
|
|
The art is DOS-era ANSI, where `1m` lifts the foreground into the bright bank and every
|
|
span after it inherits that. Neither half of that survives the trip here: rows are handed
|
|
to Rich one at a time, so nothing inherits, and on macOS Terminal bold asks for a heavier
|
|
face and leaves the colour alone. So `_state_intensity` restates brightness as the
|
|
aixterm bright bank (`9N`), which can only ever mean a colour.
|
|
|
|
These pin the part that is easy to get subtly wrong — a sequence that changes the
|
|
intensity while naming no colour at all, which is exactly how the art announces a bright
|
|
run. Both splash files are full of bare `1m`, and dropping one silently draws a whole run
|
|
in the dim bank: two spans meant to read dark then light both come out dark.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
from meshterm.ui.logo import _state_intensity
|
|
|
|
_SPLASH = Path(__file__).resolve().parent.parent / "meshterm" / "assets" / "splash"
|
|
|
|
_SGR = re.compile(r"\x1b\[([0-9;]*)m")
|
|
|
|
|
|
def _params(text: str) -> list[list[str]]:
|
|
"""Every SGR sequence in `text`, split into its parameters.
|
|
|
|
Matched as whole tokens rather than by substring: a pattern like ``[0-9;]*1`` also
|
|
matches the trailing digit of ``31``, which would read a perfectly ordinary red as a
|
|
request for bold.
|
|
"""
|
|
return [found.group(1).split(";") for found in _SGR.finditer(text)]
|
|
|
|
|
|
def _brights(text: str) -> int:
|
|
"""How many sequences in `text` name a bright-bank foreground."""
|
|
return sum(any(p.isdigit() and 90 <= int(p) <= 97 for p in parts) for parts in _params(text))
|
|
|
|
|
|
# -- a bare intensity change ----------------------------------------------------------
|
|
|
|
|
|
def test_bare_bright_lifts_the_colour_already_in_force() -> None:
|
|
"""`1m` on its own brightens the standing foreground, not just the next one named."""
|
|
out = _state_intensity("\x1b[0;37mdim\x1b[1mbright")
|
|
assert "\x1b[97m" in out, out.replace("\x1b", "ESC")
|
|
|
|
|
|
def test_bare_bright_carries_a_background_without_losing_the_foreground() -> None:
|
|
"""`1;41m` names a background and no foreground; the standing one still brightens."""
|
|
out = _state_intensity("\x1b[0;32mgreen\x1b[1;41mbright on red")
|
|
assert "\x1b[41;92m" in out, out.replace("\x1b", "ESC")
|
|
|
|
|
|
def test_returning_to_dim_restates_the_colour_in_the_dim_bank() -> None:
|
|
"""`22m` has the same problem in reverse: the run would stay in the bright bank."""
|
|
out = _state_intensity("\x1b[0;31mred\x1b[1mbright\x1b[22mdim again")
|
|
assert out.endswith("dim again")
|
|
assert "\x1b[22;31m" in out, out.replace("\x1b", "ESC")
|
|
|
|
|
|
def test_a_reset_takes_the_standing_colour_with_it() -> None:
|
|
"""After `0m` there is no colour in force, so a later `1m` has nothing to restate."""
|
|
out = _state_intensity("\x1b[0;36mcyan\x1b[0m\x1b[1mplain")
|
|
tail = out.split("plain")[0].split("\x1b[0m")[-1]
|
|
assert _brights(tail) == 0, out.replace("\x1b", "ESC")
|
|
|
|
|
|
def test_a_named_foreground_is_still_rewritten_in_place() -> None:
|
|
"""The original behaviour: `1;33m` becomes `93m`, with the bold dropped."""
|
|
out = _state_intensity("\x1b[0m\x1b[1;33myellow")
|
|
assert "\x1b[93m" in out, out.replace("\x1b", "ESC")
|
|
|
|
|
|
# -- the art itself -------------------------------------------------------------------
|
|
|
|
|
|
def test_no_splash_row_asks_a_terminal_for_bold() -> None:
|
|
"""Bold is a font weight on macOS Terminal, so none may reach the screen."""
|
|
for art in sorted(_SPLASH.glob("*.ans")):
|
|
raw = art.read_bytes().decode("cp437", errors="replace")
|
|
for number, line in enumerate(raw.split("\n"), start=1):
|
|
for parts in _params(_state_intensity(line)):
|
|
assert "1" not in parts, f"{art.name}:{number} still asks for bold"
|
|
|
|
|
|
def test_no_bright_run_in_the_art_is_silently_dropped() -> None:
|
|
"""A `1m` with a colour standing must leave a bright-bank foreground behind.
|
|
|
|
Counting brights across a file cannot catch this -- one `1m` brightens every later
|
|
sequence, so the totals stay comfortably high while individual runs go missing. The
|
|
invariant is per sequence: walk the source keeping track of whether a colour is in
|
|
force, and every sequence that turns brightness on while one is must produce a
|
|
bright-bank emission in the rewritten row.
|
|
"""
|
|
for art in sorted(_SPLASH.glob("*.ans")):
|
|
raw = art.read_bytes().decode("cp437", errors="replace")
|
|
for number, line in enumerate(raw.split("\n"), start=1):
|
|
standing, owed = None, 0
|
|
for parts in _params(line):
|
|
for part in parts:
|
|
if part in ("", "0"):
|
|
standing = None
|
|
elif re.fullmatch(r"3[0-7]", part):
|
|
standing = part
|
|
if "1" in parts and standing is not None:
|
|
owed += 1
|
|
if owed:
|
|
assert _brights(_state_intensity(line)) >= owed, (
|
|
f"{art.name}:{number} turns brightness on {owed} time(s) with a "
|
|
f"colour standing, but the rewrite emits "
|
|
f"{_brights(_state_intensity(line))} bright foreground(s)"
|
|
)
|