feat: add __fish_palette shared output palette helper

Declared --no-scope-shadowing with a bare set, so the 12 colour roles land
in the caller's scope under the same short names the consuming functions
already interpolate. Keeping the names means the conversion never edits an
output string in 33 of the 38 consumers.

No consumer is converted yet; byte-identity harness still reports 29/29.
This commit is contained in:
2026-09-07 15:00:32 -04:00
parent 51da3e9d83
commit a31a46bdc2
2 changed files with 90 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# __fish_palette
#
# DESCRIPTION
# Defines the shared terminal-output colour roles used by the
# user-facing functions in this configuration. Declared
# --no-scope-shadowing and using a bare `set`, so the variables are
# created in the CALLER's scope -- a consumer just calls it and then
# interpolates $c_head, $c_err and friends exactly as it did when the
# declarations were inline.
#
# Call it where the local declarations used to sit, once per contiguous
# block that needs the palette. set_color runs at call time, so the
# values track $TERM exactly as inline declarations did. (Measured:
# set_color output is identical across every TERM tested except
# TERM=dumb, which yields empty strings, and is unaffected by whether
# stdout is a tty or a pipe.)
#
# A role is a semantic slot, not a colour. c_flag and c_warn are both
# yellow but stay separate, as do c_ok and c_accent (both green) --
# merging either pair would foreclose ever restyling one without the
# other. c_accent is the command name in logs and smart_exit, which
# style it green where the rest of the config styles it bold.
#
# ARGUMENTS
# none
#
# EXIT STATUS
# 0 always
#
# EXAMPLE
# function mytool
# __fish_palette
# echo "$c_head""Usage:$c_reset $c_cmd""mytool$c_reset"
# end
#
# NOTES
# Calling this at top level (outside any function) creates GLOBAL
# variables. Every consumer calls it from inside a function, where the
# variables stay function-local and do not leak.
#
# functions/fish_prompt.fish deliberately does NOT use this palette. Its
# c_* values are Catppuccin hex strings passed as ARGUMENTS to set_color
# (`set_color --bold $c_green`), not captured escape sequences -- colour
# inputs rather than rendered output, a different concern.
function __fish_palette --no-scope-shadowing --description 'Define the shared output colour palette in the caller scope'
set c_reset (set_color normal)
set c_head (set_color --bold cyan)
set c_cmd (set_color --bold)
set c_arg (set_color cyan)
set c_flag (set_color yellow)
set c_warn (set_color yellow)
set c_err (set_color red)
set c_ok (set_color green)
set c_accent (set_color green)
set c_dim (set_color brblack)
set c_sel (set_color --bold magenta)
set c_hi (set_color --bold white)
end
+27
View File
@@ -97,6 +97,33 @@ function test_vault_dir_honors_override
test "$got" = /tmp/vault-override-check test "$got" = /tmp/vault-override-check
end end
function test_palette_roles_defined
functions -q __fish_palette
or begin
echo " __fish_palette is not defined"
return 1
end
# Called from inside a function, the palette must land in THIS scope.
__fish_palette
set -l missing
for role in c_reset c_head c_cmd c_arg c_flag c_warn c_err c_ok \
c_accent c_dim c_sel c_hi
if not set -q $role; or test -z "$$role"
set -a missing $role
end
end
if test (count $missing) -gt 0
echo " palette roles empty or unset: $missing"
return 1
end
# Nothing may leak to global scope.
if set -q -g c_reset
echo " __fish_palette leaked c_reset into global scope"
return 1
end
return 0
end
function functional_test_main function functional_test_main
set -l names (functions -a | string match 'test_*' | sort) set -l names (functions -a | string match 'test_*' | sort)
set -l failed 0 set -l failed 0