Files
fish-config/functions/config-settings.fish
T
rootiest ec47b0671c feat(config-settings): add Dots link toggle for user-dots symlink
Extract the user-dots symlink logic into the __fish_user_dots_link helper and
add a dedicated __fish_user_dots_symlink toggle, surfaced as a 'Dots link' bool
row on the config-settings Paths page. Unlike the opinionated categories it has
no universal/session split — it acts on a literal path.

Setting it falsy (or toggling off) stops symlink generation and removes any
existing link immediately, honoured regardless of the C2 master switch.
Creation remains a C2 startup side-effect. config.fish now calls the helper.

Docs: README, fish-config.md (C2 table, Paths page, machine-local section),
and fish-config.index updated.
2026-07-04 02:34:20 -04:00

315 lines
15 KiB
Fish
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (C) 2026 Rootiest
# SPDX-License-Identifier: AGPL-3.0-or-later
# SYNOPSIS
# config-settings [-h | --help]
#
# DESCRIPTION
# Opens an interactive full-screen TUI for managing fish config settings
# across four pages:
#
# Universal — opinionated-category toggles, persistent (set -U / set -Ue)
# Session — opinionated-category toggles, this shell only (set -g / set -eg)
# Sponge — sponge history-scrubbing settings (delay, codes, secrets …)
# Paths — scrollback log dir, log max, and user-dots path
#
# Toggle rows use ← / → (or h / l) to step OFF ← DEFAULT → ON.
# Value rows (Sponge, Paths) use Enter to edit inline; ← / h clears to default.
# Tab / Shift-Tab cycle forward / backward through pages.
# Changes apply immediately — no confirm step. Always available regardless of
# __fish_config_opinionated state.
#
# ARGUMENTS
# -h, --help Print usage and exit
#
# RETURNS
# 0 Exited normally (q or Escape pressed)
# 1 Unknown flag passed
#
# EXAMPLE
# config-settings
function config-settings --description 'Interactive TUI for managing fish config settings'
set -l c_head (set_color --bold cyan)
set -l c_cmd (set_color --bold white)
set -l c_flag (set_color yellow)
set -l c_dim (set_color brblack)
set -l c_err (set_color red)
set -l c_reset (set_color normal)
# ── Argument parsing ──────────────────────────────────
for arg in $argv
switch $arg
case -h --help
echo "$c_head""Usage:$c_reset $c_cmd""config-settings$c_reset $c_flag""[-h]$c_reset"
echo
echo " Interactive TUI for managing fish config settings."
echo " Changes apply immediately — no confirm step required."
echo
echo "$c_head""Navigation:$c_reset"
echo " $c_flag↑ ↓$c_reset or $c_flag""k j$c_reset Move cursor up / down"
echo " $c_flag← →$c_reset or $c_flag""h l$c_reset Toggle pages: OFF ← DEFAULT → ON"
echo " $c_flag""Enter$c_reset Edit value (Sponge / Paths pages)"
echo " $c_flag← / h$c_reset Clear value to default (value rows)"
echo " $c_flag""Tab / S-Tab$c_reset Next / previous page"
echo " $c_flag""q$c_reset / $c_flag""Esc$c_reset Exit"
echo
echo "$c_head""Pages:$c_reset"
echo " $c_flag""Universal$c_reset Toggles, persistent ($c_dim""set -U$c_reset)"
echo " $c_flag""Session$c_reset Toggles, this shell ($c_dim""set -g$c_reset)"
echo " $c_flag""Sponge$c_reset sponge history-scrubbing settings"
echo " $c_flag""Paths$c_reset scrollback & user-dots paths"
return 0
case '*'
echo "$c_err""Unknown option: $arg$c_reset" >&2
echo "Run $c_cmd""config-settings --help$c_reset for usage." >&2
return 1
end
end
# ── Toggle-page variables (rows 06: 6 categories + master) ───────────
set -l toggle_vars \
__fish_config_op_aliases \
__fish_config_op_autoexec \
__fish_config_op_overrides \
__fish_config_op_integrations \
__fish_config_op_logging \
__fish_config_op_greeting \
__fish_config_opinionated
# ── Value-page row metadata (parallel: var / type) ────────────────────
set -l sponge_vars sponge_delay sponge_purge_only_on_exit sponge_allow_previously_successful sponge_successful_exit_codes __fish_sponge_extra_sensitive
set -l sponge_types int bool bool list list
set -l sponge_labels Delay "Purge@exit" "Allow prev" "OK codes" "Extra secret"
set -l paths_vars __fish_scrollback_history_dir __fish_scrollback_history_max_files __fish_user_dots_path __fish_user_dots_symlink
set -l paths_types path int path bool
set -l paths_labels "Log dir" "Log max" "Dots path" "Dots link"
# Reset/blank-edit target for each value row. A non-empty entry is written
# verbatim (sponge reads sponge_delay / sponge_successful_exit_codes with no
# fallback, so they must never be left unset); an empty entry erases the var
# so its own built-in default applies (scrollback/dots paths and the
# extra-sensitive list all tolerate being unset). Bool rows are not reset
# through this path — they are a 2-state true/false with no unset state.
set -l sponge_defaults 2 '' '' 0 ''
set -l paths_defaults '' '' '' ''
# Rows per page index 0..3
set -l page_rows 7 7 5 4
set -l cur_page 0 # 0=Universal 1=Session 2=Sponge 3=Paths
set -l cur_row 0
set -l panel_h 16
set -l last_cols $COLUMNS
# ── Terminal setup ────────────────────────────────────
printf '\e[?25l' # hide cursor
trap 'printf "\e[?25h"; set -g __config_settings_exit 1' INT
# ── Draw dispatch (page 0/1 = toggle table; 2/3 = value page) ─────────
function __cs_dispatch_draw --no-scope-shadowing
switch $cur_page
case 0
__config_settings_draw $cur_row universal $toggle_vars
case 1
__config_settings_draw $cur_row session $toggle_vars
case 2
__config_settings_draw_value $cur_row sponge
case 3
__config_settings_draw_value $cur_row paths
end
end
__cs_dispatch_draw
# ── Event loop ────────────────────────────────────────
# __config_settings_read_key reads a single keypress from /dev/tty in raw
# mode and returns a normalized token (up/down/tab/space/escape/quit or a
# literal char). It bypasses fish's `read`, whose line editor swallows Tab
# and arrow keys and prints a `read> ` prompt — unusable for a TUI.
while true
# Check for Ctrl-C signal (trap sets this flag during redraw, when the
# terminal is briefly back in cooked mode and SIGINT can fire).
if set -q __config_settings_exit
set -eg __config_settings_exit
break
end
set -l key (__config_settings_read_key)
or break # not a TTY — exit instead of spinning
set -l did_redraw 0
switch $key
case up k
set cur_row (math "max(0, $cur_row - 1)")
case down j
# Hoist the page index: fish cannot expand a command-substitution
# index inside a quoted math string.
set -l pidx (math $cur_page + 1)
set cur_row (math "min($page_rows[$pidx] - 1, $cur_row + 1)")
case tab
set cur_page (math "($cur_page + 1) % 4")
set cur_row 0
case backtab
set cur_page (math "($cur_page + 3) % 4")
set cur_row 0
case right l
if test $cur_page -le 1
# Toggle page: step toward ON
set -l scope universal
test $cur_page -eq 1; and set scope session
set -l varname $toggle_vars[(math $cur_row + 1)]
set -l cur_val (__config_settings_get_val $varname $scope)
set -l next_val on
test "$cur_val" = off; and set next_val DEFAULT
__config_settings_apply $varname $scope $next_val
else
# Value pages: bool rows are 2-state (true/false). → sets
# true. Sponge reads its bools with no fallback; the Paths
# "Dots link" bool drives __fish_user_dots_link on change.
set -l v_vars $sponge_vars
set -l v_types $sponge_types
if test $cur_page -eq 3
set v_vars $paths_vars
set v_types $paths_types
end
set -l ridx (math $cur_row + 1)
if test "$v_types[$ridx]" = bool
set -U $v_vars[$ridx] true 2>/dev/null
test "$v_vars[$ridx]" = __fish_user_dots_symlink
and __fish_user_dots_link
end
end
case left h
if test $cur_page -le 1
set -l scope universal
test $cur_page -eq 1; and set scope session
set -l varname $toggle_vars[(math $cur_row + 1)]
set -l cur_val (__config_settings_get_val $varname $scope)
set -l next_val off
test "$cur_val" = on; and set next_val DEFAULT
__config_settings_apply $varname $scope $next_val
else
# Value pages: bool rows set false; other value rows reset to
# their default (a literal value, or erase when the var
# tolerates being unset — see sponge_defaults/paths_defaults).
set -l v_vars $sponge_vars
set -l v_types $sponge_types
set -l v_defaults $sponge_defaults
if test $cur_page -eq 3
set v_vars $paths_vars
set v_types $paths_types
set v_defaults $paths_defaults
end
set -l ridx (math $cur_row + 1)
set -l varname $v_vars[$ridx]
set -l vtype $v_types[$ridx]
if test "$vtype" = bool
set -U $varname false 2>/dev/null
test "$varname" = __fish_user_dots_symlink
and __fish_user_dots_link
else
__config_settings_set_value $varname $vtype "$v_defaults[$ridx]"
end
end
case enter
if test $cur_page -ge 2
set -l v_vars $sponge_vars
set -l v_types $sponge_types
set -l v_defaults $sponge_defaults
if test $cur_page -eq 3
set v_vars $paths_vars
set v_types $paths_types
set v_defaults $paths_defaults
end
set -l ridx (math $cur_row + 1)
set -l varname $v_vars[$ridx]
set -l vtype $v_types[$ridx]
# Only path/int/list rows are editable; bool rows toggle with ←/→.
if test "$vtype" != toggle -a "$vtype" != bool
# Inline editor: edit in-place in the row's value field
# using the raw key reader — no fish `read` / `read>`
# prompt, and the panel cleans itself up on exit. The
# buffer is pre-filled with the current value; clearing it
# and pressing Enter reverts to the row's default.
set -l page sponge
test $cur_page -eq 3; and set page paths
set -l buf (__config_settings_get_raw $varname)
test "$buf" = DEFAULT; and set buf ""
set -l committed 0
while true
set -l pml (math --scale=0 "($last_cols + 78) / 2")
set -l eh (math --scale=0 "$panel_h * max(1, ceil($pml / $COLUMNS))")
printf '\e[%dA\e[J' $eh
set last_cols $COLUMNS
__config_settings_draw_value $cur_row $page edit "$buf"
set -l ek (__config_settings_read_key)
or break
switch $ek
case enter
set committed 1
break
case escape
break
case backspace
set buf (string sub -s 1 -e -1 -- "$buf")
case space
set buf "$buf "
case up down left right tab backtab quit ''
# ignored while editing
case '*'
set buf "$buf$ek"
end
end
if test $committed -eq 1
# Empty buffer reverts to the row default (a value, or
# erase when the var tolerates being unset).
if test -n "$buf"
__config_settings_set_value $varname $vtype "$buf"
else
__config_settings_set_value $varname $vtype "$v_defaults[$ridx]"
end
end
# Redraw the normal panel in place of the editor.
set -l pml (math --scale=0 "($last_cols + 78) / 2")
set -l eh (math --scale=0 "$panel_h * max(1, ceil($pml / $COLUMNS))")
printf '\e[%dA\e[J' $eh
set last_cols $COLUMNS
__cs_dispatch_draw
set did_redraw 1
end
end
case q Q quit escape
break
end
# Skip redraw entirely when the key reader timed out with no resize
if test -z "$key" -a "$COLUMNS" = "$last_cols"
continue
end
# Skip redraw if the Enter handler already redrew (e.g. after path edit)
if test $did_redraw -eq 1
continue
end
# Wrap-aware erase: a panel drawn on a wider terminal has longer lines
# (due to center-padding) that wrap into extra physical rows when the
# terminal narrows. 78 = widest box (IW=76+2); the formula gives the
# worst-case old line width for any tier drawn at last_cols.
set -l prev_max_lw (math --scale=0 "($last_cols + 78) / 2")
set -l erase_h (math --scale=0 "$panel_h * max(1, ceil($prev_max_lw / $COLUMNS))")
printf '\e[%dA\e[J' $erase_h
set last_cols $COLUMNS
__cs_dispatch_draw
end
# ── Cleanup ───────────────────────────────────────────
trap - INT # remove the signal handler
set -l prev_max_lw (math --scale=0 "($last_cols + 78) / 2")
set -l erase_h (math --scale=0 "$panel_h * max(1, ceil($prev_max_lw / $COLUMNS))")
printf '\e[%dA\e[J' $erase_h # erase the panel (wrap-aware)
printf '\e[?25h' # restore cursor
functions --erase __cs_dispatch_draw
end