diff options
| author | user <user@clank> | 2026-09-22 12:42:15 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-22 12:42:15 +0200 |
| commit | faa3fb6ee575a900abc574b0f9ed5ff291761837 (patch) | |
| tree | ce644998c144687e205d1f4e97e6f999de98723b /tests/test_dump_config.py | |
| parent | tabs: idle tabs you can actually read across the room (diff) | |
| download | slosh-faa3fb6ee575a900abc574b0f9ed5ff291761837.tar.gz slosh-faa3fb6ee575a900abc574b0f9ed5ff291761837.tar.xz slosh-faa3fb6ee575a900abc574b0f9ed5ff291761837.zip | |
theme: a name and a directory, not sixty colours in your config
Wearing a theme meant pasting its `theme { }` block into config.kdl, or
including a path to a file you had copied there yourself. Both make the
palette a literal in your config, and a literal is the one thing you cannot
change cheaply: not from a script, not from a running session, not by a list
of what is available, and never again from upstream -- the colours you pasted
are the colours that theme had the day you pasted them.
So a theme is a file in a directory now, and a config names it:
theme_name "phosphor"
theme { frame_focus "#00ff88" } // ...but that one colour is mine
theme_name is applied after the includes and before everything the file says
itself, which is the rule includes already follow and for the same reason: the
loader reads a document by asking it for names rather than walking it in order,
so "here" is not a position it could honour, and "what you asked for is the
base, what you wrote is yours" is the rule worth having.
Two directories, yours first: `themes/` beside the root config -- beside the
file that was loaded, so a config somewhere else (SLOSH_CONFIG, or one handed
to --check) takes its themes with it -- and then <prefix>/share/slosh/themes,
compiled in from PREFIX. install(1) has been putting themes there all along
and nothing could read them; now a shipped theme is nameable without being
copied, and a file of yours with the same name shadows it, which is how a
root-owned theme gets edited.
A name, not a path. The directory is what makes a theme switchable, listable
and shadowable, and a path silently does none of those, so `theme_name
"../x.kdl"` is refused with a line pointing at `include`, which still works
and is still the answer for a file somewhere a directory of names cannot
reach. A theme that is not there is one complaint and no more (D9): the rest
of the config applies, because losing your keybindings over a mistyped colour
scheme would be the wrong trade. A theme file is read by the same loader, so
it is watched like any other: saving one reloads the session wearing it, and
the derivations (tab_status_fg, tab_idle_bg) run over the theme's own colours
exactly as before.
Switching without editing anything, over the socket a session already has:
slosh -s main cmd theme # amber default* ...
slosh -s main cmd '{"cmd":"theme","name":"amber"}' # now
slosh -s main cmd '{"cmd":"theme","name":"amber","save":true}'
The session holds the name over later reloads, so saving your config for an
unrelated reason does not put the old colours back; `save` writes the
theme_name line into the file, replacing the one already there and leaving
every other line and comment alone -- dumping the whole config back over
itself would reformat a hand-written file and drop its comments, which is not
a fair price for changing a colour scheme. A name is tried on a throwaway
config first, so a typo costs nothing. An empty name gives the answer back to
the file.
--dump-config stops writing colours: it writes the theme's name and the
colours a config overrode by hand, so a dumped config keeps following its
theme instead of freezing it. --dump-theme is the other half and is new: the
resolved palette as a theme file, which is how you start one from the session
in front of you rather than from an empty file. It is also what keeps
contrib/themes/default.kdl honest -- that file is now the annotated reference
(the commented palette moved there out of config.kdl, where it was 119 lines
of colour in the middle of the prose), and a test compares it against the
compiled-in one.
While in there: tests/test_themes.py's "every theme sets every colour" guard
had been grepping for `&c->`, the spelling before the colour table moved to
offsetof, so it had been finding zero colours and passing for months. Fixed,
with the derived and opt-in colours exempted for the reasons they are derived
and opt-in, and with a check that the guard found a table at all.
Diffstat (limited to 'tests/test_dump_config.py')
| -rw-r--r-- | tests/test_dump_config.py | 37 |
1 files changed, 34 insertions, 3 deletions
diff --git a/tests/test_dump_config.py b/tests/test_dump_config.py index 67679a1..611578c 100644 --- a/tests/test_dump_config.py +++ b/tests/test_dump_config.py @@ -32,10 +32,18 @@ def dump(config=None): ).stdout +def theme_dump(config=None): + env = dict(os.environ) + env["SLOSH_CONFIG"] = config or "/nonexistent/slosh.kdl" + return subprocess.run( + [BIN, "--dump-theme"], capture_output=True, text=True, env=env + ).stdout + + def test_it_dumps_something_that_looks_like_a_config(): text = dump() check("it has content", len(text.splitlines()) > 100, str(len(text))) - for expect in ("gap ", "theme {", "states {", "keys {", "prefix "): + for expect in ("gap ", "states {", "keys {", "prefix "): check(f"it has {expect!r}", expect in text, text[:200]) @@ -102,10 +110,33 @@ def test_every_key_the_parser_knows_is_in_it(): ) -def test_every_theme_colour_is_in_it(): +def test_the_config_dump_names_a_theme_rather_than_a_palette(): + """Colour left this file when themes got names. A seed config full of + resolved colours pins the palette at the moment it was written, and every + later improvement to the theme it came from stops arriving -- so the dump + offers the two settings that *can* follow a theme, and no block.""" + text = dump() + for expect in ("// theme_name", "// theme_dir"): + check(f"it offers {expect!r}", expect in text, text[:400]) + check( + "it pins no palette", + not re.search(r"^theme \{", text, re.M), + text[:400], + ) + check( + "and names no theme of its own: a default is not a theme", + not re.search(r"^theme_name", text, re.M), + text[:400], + ) + + +def test_every_theme_colour_is_in_the_theme_dump(): + """The same guard, moved to where the colours went: --dump-theme is the + file you start a theme from, so a colour missing from it is a colour + nobody can change by copying what they are wearing.""" src = open(SRC).read() colours = set(re.findall(r'\{"([a-z_]+)", offsetof\(config_t', src)) - text = dump() + text = theme_dump() # A colour that is *unset* is the terminal's own, and a theme block has no # spelling for that -- parse_color takes #rrggbb and nothing else. Those # are written commented out, so the dump still names the key while loading |
