feat(config-settings): replace the ANSI renderer with a curses front-end #141
No Reviewers
Labels
Clear labels
Area/CI
Area/Completions
Area/Components
Area/Config
Area/Docs
Area/Functions
Area/Integrations
Area/Prompt & Theme
Area/Scripts
Area/Tests
Compat/Breaking
Kind/Bug
Kind/Chore
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Performance
Kind/Refactor
Kind/Security
Kind/Testing
good first issue
help wanted
.github/workflows and repository automation
completions/ - tab-completion scripts
The opinionated-component system (C1-C6)
config.fish and conf.d/ - startup and environment
docs/manual/ and the generated manual, man page, and site
functions/ - user-facing functions and helpers
integrations/ - third-party tool wiring
themes/ and prompt appearance
scripts/ - standalone helper scripts
tests/ - the syntax lint and functional suite
Breaking change that won't be backward compatible
Something is not working
Tooling, dependencies, and housekeeping
Documentation changes
Improve existing functionality
New functionality
Makes existing behavior faster or lighter
Restructures code without changing behavior
This is security issue
Issue or pull request related to testing
Well-scoped and self-contained; a good place to start
Maintainer is looking for someone to pick this up
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Medium
3
The priority is medium
Priority
Low
4
The priority is low
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Assignees
rootiest (Rootiest)
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: rootiest/fish-config#141
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Replaces the hand-rolled ANSI renderer behind
config-settingswith a Pythoncursesfront-end, and adds a filter that reaches sub-categories.config-settingsis now a launcher forscripts/config-settings-tui.py. The seven fish files that did cursor arithmetic are gone, along with the golden harness that had to pin their byte-exact output.Why
Thirty-five commits went into the old renderer, and four of them fixed the same bug in different disguises:
608b022(fish'sreadprompt shifted the cursor),4210f3b(a shadow warning on stderr shifted the cursor),93fc5e0and3c4f720(line wrap broke the erase height). All four have one root cause — the panel was drawn inline in the scrollback and navigated with relative cursor moves (\e[<n>A), which makes cursor position global mutable state that any stray byte can corrupt.curses uses the alternate screen with absolute addressing, so that entire class is unreachable. It also diffs its virtual screen against the physical one and emits only changed cells, which is what
__config_settings_diff_redraw.fishwas reimplementing by hand.Architecture
The TUI is a child process, so it can neither read the session's global variables nor write them:
Every edit is emitted as a call to
__config_settings_applyor__config_settings_set_value, so list splitting, theSCROLLBACK_HISTORY_*export mirror,__fish_user_dots_linkand the shadow-warning suppression all stay in the fish helpers that already owned them. 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.What changed for the user
New — the filter reaches sub-categories.
/filters the current page on label and description. On the Universal and Session pages it also searches every category's sub-categories, listing hits asCategory › Sub, so a sub-category can be toggled without drilling into its parent first.Also new: a
?help overlay, mouse selection, a page sidebar, and a drill-down page that leads with the category's own toggle.Behaviour change — edits apply on exit, not per keypress. This is forced by the child-process boundary, not a preference: a child cannot reach into its parent shell to set a global. The status bar shows a pending count, and
qapplies.New dependency —
python3withcurses. That module is stdlib on Arch, Fedora and a full Debian/Ubuntupython3;python3-minimalalone does not carry_curses. The launcher checks for both and names what is missing rather than failing inside the renderer. Called out in the README's Installation section.Removed
functions/__config_settings_draw{,_subcat,_value}.fishfunctions/__config_settings_{frame,pagetab,read_key,diff_redraw}.fishtests/test-config-settings-render.fish+ its goldenGone with them: the four width tiers, the wrap-aware erase arithmetic, the
stty/dd/odraw key reader, the panel-height bookkeeping and the hand-written redraw differ. Net: 1147 insertions, 6475 deletions, with more features than before.The golden harness is deliberately not replaced. It existed because the fish renderer's field widths, dash counts and pad targets were hand-tuned arithmetic that any refactor had to reproduce byte-for-byte. curses owns that now, so there is nothing left to pin. The seam that does need pinning — dump in, fish script out — is covered instead.
Verification
416/416assertions intests/run-tests.fish(the golden suite's cases removed, 7 new ones added covering the state dump, the emitted script, and the no-TTY guard)76/76indocs/verify-manual.py227/227files pass the syntax lint$HOME: the Universal page writes universal scope only (U1/G0), the Session page global scope only (U0/G1), exit 0Notes for review
functions/__config_settings_state.fishdiscovers variable names withset --namesand a prefix filter rather than carrying a list, so a new sub-category needs no edit there — only in__config_settings_subcats. Theconf.dregistry data table shares theop_prefix and is explicitly excluded; there is a test for that.DEFAULT, which is indistinguishable from a config where nothing is set. That is a wrong answer rather than a missing one.docs/fish-config.mdand.1are regenerated by CI on push, so they are not in this branch.Add scripts/config-settings-tui.py, a stdlib-curses prototype of the config-settings interface, plus a gate that runs its --self-test. The backend is stubbed: values live in an in-memory dict and nothing is read from or written to fish variables. This is here to evaluate the render engine and the interaction model before committing to a rewrite. Why curses rather than more ANSI arithmetic: - No flicker, structurally. curses diffs its virtual screen against the physical one and emits only the changed cells, which is what __config_settings_diff_redraw.fish reimplements by hand. - Alternate screen plus absolute addressing. Stray output cannot desync the display, so the bug class behind608b022(fish's read prompt),4210f3b(a shadow warning on stderr),93fc5e0and3c4f720(line wrap breaking the erase height) cannot occur at all. - Resize is a repaint rather than wrap-factor arithmetic. - Overlays, panes, live filtering and mouse input cost a few lines each. The layout departs from the current single panel: a page sidebar with a live filter on the left, a scrolling detail pane on the right, a help overlay on '?', and mouse selection. Sub-category drill-down, the tri-state badges and the Sponge/Paths value rows all carry over. Dependency note: python3 with the curses module. That is stdlib on Arch, Fedora and a full Debian/Ubuntu python3; python3-minimal alone does not carry _curses, so the test asserts the import. The prototype is not wired into config-settings and nothing existing changed.