Files
fish-config/functions/__config_settings_state.fish
T
rootiest 078d01666e feat(config-settings): replace the ANSI renderer with a curses front-end
config-settings is now a launcher for scripts/config-settings-tui.py,
drawn with Python's stdlib curses. The seven fish files that hand-rolled
the ANSI renderer are gone, along with the golden harness that had to pin
their byte-exact output.

The TUI is a child process, so it can neither read the session's global
variables nor write them. State goes in as a dump from the new
__config_settings_state; the edits come back as a fish script that
config-settings sources, which is what lets the Session page's `set -g`
land in the caller's shell instead of in a child that is about to exit.
Every edit is emitted as a call to __config_settings_apply or
__config_settings_set_value, so list splitting, the SCROLLBACK_HISTORY_*
export mirror and the shadow-warning suppression all stay in the fish
layer that already owned them.

The consequence, and the one behaviour change: edits are applied in one
batch on exit rather than on each keypress. The status bar shows a
pending count.

New: `/` filters the current page, and on the Universal and Session pages
it reaches into every category's sub-categories, listing hits as
"Category › Sub" so a sub-category can be toggled without drilling into
its parent first. Also a `?` help overlay, mouse selection, and a
drill-down page that leads with the category's own toggle.

Gone with the renderer: the four width tiers, the wrap-aware erase
arithmetic, the stty/dd/od raw key reader, the panel-height bookkeeping
and the hand-written redraw differ. curses owns all of it, and the alt
screen plus absolute addressing makes the desync class behind 608b022,
4210f3b, 93fc5e0 and 3c4f720 unreachable.

The sub-category taxonomy is NOT duplicated in Python: it travels in the
state dump, still sourced from __config_settings_subcats. The category,
Sponge and Paths row tables move into Python, consolidating the two
copies the fish renderers kept.

Dependency: python3 with curses. Stdlib on Arch, Fedora and a full
Debian/Ubuntu python3; python3-minimal alone lacks _curses. The launcher
checks for both and names what is missing. Called out in the README.

Verified: 416/416 assertions, plus a live end-to-end in a sandbox HOME
confirming the Universal page writes universal scope only (U1/G0) and the
Session page global scope only (U0/G1).
2026-09-09 14:50:26 -04:00

77 lines
3.4 KiB
Fish

# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# DEPENDENCIES
# __config_settings_get_val, __config_settings_get_raw,
# __config_settings_subcats
#
# SYNOPSIS
# __config_settings_state
#
# DESCRIPTION
# Dumps everything scripts/config-settings-tui.py needs to render the
# settings TUI: the current value of every opinionated-component,
# sponge, scrollback and user-dots variable, plus the sub-category
# taxonomy from __config_settings_subcats.
#
# Two record types, separated by RS (0x1e), fields separated by US
# (0x1f). Both are ASCII control characters, so no value these variables
# can hold needs escaping on the way through:
#
# var<US><scope><US><name><US><value>
# sub<US><category_var><US><slug><US><label><US><description>
#
# Only variables that are actually set are emitted; the TUI renders an
# absent variable as DEFAULT. That is what keeps the variable list out of
# this function -- names are discovered with `set --names` and filtered by
# prefix, so a new sub-category needs no edit here, only in
# __config_settings_subcats.
#
# Toggle variables are dumped once per scope (universal and session), since
# the TUI edits those scopes independently. Sponge and Paths variables are
# universal-only and dumped as the value the running shell resolves,
# space-joined for list variables.
#
# EXIT STATUS
# 0 Always
#
# RETURNS
# The RS/US-separated state dump, printed to stdout
#
# EXAMPLE
# __config_settings_state | string split \x1e
function __config_settings_state --description 'Dump config-settings state for the curses TUI'
# ── Variable values ───────────────────────────────────────────────────
for name in (set --names)
switch $name
case '__fish_config_op_registry_*'
# conf.d data table, not a setting -- shares the op_ prefix.
continue
case '__fish_config_op_*' __fish_config_opinionated
# Toggles: both scopes, independently editable.
for scope in universal session
set -l val (__config_settings_get_val $name $scope)
test "$val" = DEFAULT; and continue
printf '%s\x1f%s\x1f%s\x1f%s\x1e' var $scope $name $val
end
case 'sponge_*' __fish_sponge_extra_sensitive \
'__fish_scrollback_history_*' __fish_user_dots_path \
__fish_user_dots_symlink
# Value rows: universal-only, list variables space-joined.
set -l val (__config_settings_get_raw $name)
test "$val" = DEFAULT; and continue
printf '%s\x1f%s\x1f%s\x1f%s\x1e' var universal $name $val
end
end
# ── Sub-category taxonomy ─────────────────────────────────────────────
for cvar in __fish_config_op_aliases __fish_config_op_autoexec \
__fish_config_op_overrides __fish_config_op_integrations \
__fish_config_op_logging __fish_config_op_greeting
for row in (__config_settings_subcats $cvar)
set -l f (string split -- \t $row)
printf '%s\x1f%s\x1f%s\x1f%s\x1f%s\x1e' sub $cvar $f[1] $f[2] $f[3]
end
end
end