diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb0ad6d..e61a79b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -243,8 +243,21 @@ Write doc-headers as plain text — no backticks. `-a/--all`, `__fish_config_op_aliases` and `~/.config/fish/config.fish` are typed bare, because the header is also read as-is by `config-help` and by anyone opening the file. `docs/codespans.py` adds the inline code spans -the docs site wants when it renders, so the SSOT never carries them; see -`docs/site/README.md` for which shapes it recognises. +when it renders, so the SSOT never carries them; see +`docs/site/README.md` for which shapes it recognises. That pass runs for +every output — the site, the man page and `config-help` — so a token is +typeset the same way wherever it is read. + +Two rules apply to backticks you write under `docs/manual/` as well: + +- **Never inside an indented block.** A four-space block is verbatim in + every renderer, so a backtick there is a literal character on the page + rather than markup. +- **Never wrapped across a line break.** Markdown accepts a span split + over two lines, but `config-help` pairs backticks one line at a time + and would show the halves literally. Reflow the sentence instead. + +`docs/verify-manual.py` enforces both. ## Testing diff --git a/docs/build-manual.py b/docs/build-manual.py index e74802b..e4b81ad 100644 --- a/docs/build-manual.py +++ b/docs/build-manual.py @@ -95,6 +95,16 @@ def build_concat(root: Path) -> str: with no frontmatter fences and no Astro-visible frontmatter key. When present, its contents are re-emitted byte-for-byte as the leading `---`-fenced block, ahead of every heading. + + Bodies go through `codespans` here so a token is typeset the same way + in every output: `tmux` and `local.fish` are wrapped on the site by + that pass, and without it the man page marked only what the SSOT + happened to backtick by hand. Section 5 is unaffected -- its entries + arrive as indented verbatim blocks, which `codespans` leaves alone and + pandoc already sets in a monospace font. + + Only bodies are passed: the pandoc metadata block above is not prose + and must survive byte-for-byte. """ entries = build_entries(mt.parse_functions(FUNCTIONS)) chunks: list[str] = [] @@ -117,6 +127,7 @@ def build_concat(root: Path) -> str: body = re.sub(r"\n*", "", body, flags=re.DOTALL) body = re.sub(r"\n*", "", body, flags=re.DOTALL) body = re.sub(r"\[([^\]]+)\]\(/[^)]+\)", r"\1", body) + body = codespans.add_code_spans(body, _code_vocabulary()) chunks.append(mt.shift_headings(body, depth)) return "\n\n".join(chunks) + "\n" @@ -495,12 +506,15 @@ def _code_vocabulary() -> codespans.Vocabulary: def prettify(body: str, entry_name: str | None = None) -> str: """Rewrite a body's indented code blocks and labeled asides for the website. - Site-only: the man page and `config-help` keep reading the untouched - SSOT, where the indented form and the `LABEL:` text are exactly what - pandoc/`config-help` want. The same applies to the inline code spans - added last: `-a/--all` and `__fish_config_op_aliases` are authored bare - so the `functions/*.fish` headers stay readable as plain text, and the - backticks the site wants are put on here rather than in the SSOT. + The block and aside rewrites are site-only: the man page and + `config-help` keep reading the untouched SSOT, where the indented form + and the `LABEL:` text are exactly what pandoc/`config-help` want. + + The inline code spans added last are not site-only. `-a/--all` and + `__fish_config_op_aliases` are authored bare so the `functions/*.fish` + headers stay readable as plain text, and the backticks every output + wants are put on here rather than in the SSOT -- `build_concat()` runs + the same pass for the man page and `config-help`. """ out: list[str] = [] block: list[str] = [] diff --git a/docs/codespans.py b/docs/codespans.py index a14d58a..4e0d34c 100644 --- a/docs/codespans.py +++ b/docs/codespans.py @@ -18,8 +18,9 @@ touched, so the man page and `config-help` keep the plain-text form. Everything here is conservative by construction: leaving a token alone is always safe and wrapping the wrong one is not, so every rule bails out the moment it is unsure. The regions that must never be rewritten -- fenced -blocks, existing code spans, link targets, URLs, JSX attributes, -`` bodies, headings -- are recognised first and passed through. +blocks, indented code blocks, existing code spans, link targets, URLs, JSX +attributes, `` bodies, headings -- are recognised first and +passed through. """ import functools @@ -359,6 +360,14 @@ FILE_TREE_OPEN = " bool: """True for a line that must be passed through untouched. @@ -488,8 +497,9 @@ def add_code_spans(text: str, vocab: Vocabulary = EMPTY_VOCABULARY) -> str: """Wrap code-shaped tokens in `text` in inline code spans. `text` is a rendered page body (no frontmatter). Fenced blocks, - `` bodies, headings, component markup, existing code spans, - link targets and URLs are left exactly as they are. + indented code blocks, `` bodies, headings, component markup, + existing code spans, link targets and URLs are left exactly as they + are. """ scanner = _scanner(vocab) atom_re = _atom_re(vocab) @@ -510,6 +520,8 @@ def add_code_spans(text: str, vocab: Vocabulary = EMPTY_VOCABULARY) -> str: if FILE_TREE_CLOSE in line: in_tree = False continue + if line.startswith(INDENTED_CODE): + continue eligible[i] = not _skip_line(line) # Command columns are a property of a whole table, so the contiguous diff --git a/docs/fish-config.md b/docs/fish-config.md index 1f555c8..fd73508 100644 --- a/docs/fish-config.md +++ b/docs/fish-config.md @@ -32,10 +32,10 @@ A production-grade Fish shell configuration targeting Fish 4.x. It provides: - Drop-in replacements for common Unix tools (`ls`, `cat`, `rm`, `du`, `ping`, `less`) - Deep Kitty and WezTerm terminal integration: tab/window/pane management from the command line -- Optional session logging: terminal scrollback, tmux/zellij panes, and - paru/yay output captured to `~/.terminal_history` (off by default; see C5 Logging) +- Optional session logging: terminal scrollback, `tmux`/`zellij` panes, and + `paru`/`yay` output captured to `~/.terminal_history` (off by default; see C5 Logging) - Automatic Python virtualenv activation on directory change -- Cross-platform package management via pkg and fish-deps +- Cross-platform package management via pkg and `fish-deps` - AI scaffolding helpers for Claude Code and Antigravity - Catppuccin Mocha color theme throughout @@ -133,8 +133,8 @@ The configuration uses a structured file tree: # 1. CONFIGURATION VARIABLES -These variables are exported from config.fish on every interactive session. -Override them in local.fish (see Section 10, Personalization). +These variables are exported from `config.fish` on every interactive session. +Override them in `local.fish` (see Section 10, Personalization). ## Environment Directories (XDG) @@ -290,7 +290,7 @@ Directories prepended to PATH in this order (first wins): | `~/scripts` | Personal shell scripts | | `~/bin` | Cargo binaries (appended — lowest priority) | | `$BUN_INSTALL/bin` | Bun runtime and global packages | -| `$NPM_CONFIG_PREFIX/bin` | Global npm packages | +| `$NPM_CONFIG_PREFIX/bin` | Global `npm` packages | | `~/.lmstudio/bin` | LM Studio CLI | | `~/.resend/bin` | Resend CLI | | `~/.fzf/bin` | `fzf` binary (git-installed) | @@ -306,7 +306,7 @@ TIP: This standard PATH setup is gated behind the opinionated component override # 3. KEY BINDINGS -The shell uses Vi key bindings (fish_vi_key_bindings). All custom bindings +The shell uses Vi key bindings (`fish_vi_key_bindings`). All custom bindings are active in Insert, Normal, and Visual modes unless noted. Binding Action @@ -561,7 +561,7 @@ as keybindings, but they all serve the same purpose. ## 4.11 Shell Aliases -These aliases are defined in conf.d/tricks.fish via alias (which creates Fish +These aliases are defined in `conf.d/tricks.fish` via alias (which creates Fish functions). They are active in all interactive sessions. Abbreviation Description @@ -800,7 +800,7 @@ functions). They are active in all interactive sessions. Wraps mv to automatically collapse nested directories of the same name. When extracting archives results in redundant structures (e.g., - themes/themes/), calling `mv themes/themes themes` will gracefully + themes/themes/), calling mv themes/themes themes will gracefully move the inner contents up one level and remove the empty outer shell. Opinionated component (C1): when disabled via __fish_config_op_aliases, @@ -1327,10 +1327,10 @@ functions). They are active in all interactive sessions. Optional btop, dust, duf, prettyping, go, lazygit, lazydocker, docker, yt-dlp, screen — single-purpose wrapper conveniences that only matter if you - already use that tool; skipped by `install`/`sync` + already use that tool; skipped by install/sync unless --optional (or --all) is passed Terminal Emulators kitty, wezterm — only matter if one of them is - your actual terminal; skipped by `install`/`sync` + your actual terminal; skipped by install/sync unless --terminals (or --all) is passed Integrations wakatime, tailscale @@ -1565,13 +1565,13 @@ functions). They are active in all interactive sessions. Runs, lists, inspects, re-attaches to, and terminates named background jobs using tmux or GNU screen as the process engine. Unlike bkg and detach, which discard output, a jobrunner job keeps a live terminal you - can return to later — it survives closing the shell, and `attach` + can return to later — it survives closing the shell, and attach restores it in any subsequent session. Run and manage named background jobs. Jobs are detached from the shell and backed by tmux (preferred) or GNU screen. - If the job name is omitted when starting a new job (e.g. `jobrunner sleep 1`), - a memorable, random name (like `sleepy-badger`) will be generated. + If the job name is omitted when starting a new job (e.g. jobrunner sleep 1), + a memorable, random name (like sleepy-badger) will be generated. Exit Status: 0 Command succeeded, or no jobs are running @@ -1582,7 +1582,7 @@ functions). They are active in all interactive sessions. Detach from an attached job with Ctrl-A then D; the job keeps running. Commands are executed directly rather than through a shell, so pipes and redirections must be wrapped explicitly, e.g. - `jobrunner run sync fish -c 'a | b'`. + jobrunner run sync fish -c 'a | b'. Example: jobrunner run -n build make -j8 @@ -1893,7 +1893,7 @@ functions). They are active in all interactive sessions. Notes: The exit builtin is wired to smart_exit for interactive sessions. Typing - `exit` or Ctrl+D behaves identically to calling smart_exit directly. + exit or Ctrl+D behaves identically to calling smart_exit directly. Example: smart_exit @@ -2122,7 +2122,7 @@ functions). They are active in all interactive sessions. scripts/cli-agent.md to $XDG_CONFIG_HOME/aichat/roles/cli.md. Inherits every aichat flag and tab completion (--wraps aichat); passing --role/-r overrides the default role, so qc forwards to aichat unchanged. The - function is only defined when aichat is installed. Run `qc --help` for + function is only defined when aichat is installed. Run qc --help for aichat's full flag reference with the command name rewritten to qc. Arguments: @@ -2350,7 +2350,7 @@ functions). They are active in all interactive sessions. in the default browser via xdg-open — deep links to a section aren't supported there, so if a keyword is given a note points you to the site's search box instead. Pass --man / -m to open the compiled man page - (docs/fish-config.1) via `man -l`; if a section keyword is given, the + (docs/fish-config.1) via man -l; if a section keyword is given, the pager opens at the nearest match. Pass --help or -h for usage and the navigation key reference. @@ -2369,9 +2369,9 @@ functions). They are active in all interactive sessions. Otherwise, the manual is shown via the resolved pager (not captured stdout). Notes: - The preferred invocation is `help config [...]` — this function is + The preferred invocation is help config [...] — this function is registered as a handler in the help wrapper so that syntax works - transparently. Direct `config-help` calls are also valid. + transparently. Direct config-help calls are also valid. Example: config-help @@ -2476,7 +2476,7 @@ functions). They are active in all interactive sessions. Pulls the latest fish shell configuration from the upstream repository into ~/.config/fish. Git output is suppressed; status is reported through colored messages. After a successful pull the function prints a - short summary of changed files; run `exec fish` to reload the shell. + short summary of changed files; run exec fish to reload the shell. Arguments: -h, --help Show this help message and exit @@ -2570,11 +2570,11 @@ functions). They are active in all interactive sessions. Synopsis: kitty-logging [install | uninstall | status | dismiss] [-h] Manages the fish-config Kitty scrollback watcher that powers C5 logging. - `install` symlinks the canonical watcher into the Kitty config dir (so it + install symlinks the canonical watcher into the Kitty config dir (so it always tracks the source) and wires it into kitty.conf via a sentinel-marked managed block, commenting out any conflicting active - watcher line to avoid double-capture. `uninstall` reverses it. `status` - reports wiring, installed watcher version, and C5 logging state. `dismiss` + watcher line to avoid double-capture. uninstall reverses it. status + reports wiring, installed watcher version, and C5 logging state. dismiss silences the per-session setup reminder. Runtime capture stays governed by the C5 .logging_disabled sentinel, so @@ -2654,9 +2654,9 @@ functions). They are active in all interactive sessions. Generates a random, memorable string using a sequence of specified word categories and formatting modifiers. Words are pulled from curated - plain-text databases bundled in `data/words/`. + plain-text databases bundled in data/words/. - Modifiers like `--separator` and `--case` are evaluated sequentially and + Modifiers like --separator and --case are evaluated sequentially and apply only to the components that follow them. Supported Components: @@ -2674,7 +2674,7 @@ functions). They are active in all interactive sessions. 1 Unknown category or missing word list file Notes: - Falls back to `random choice` if GNU `shuf` is missing, but `shuf` is + Falls back to random choice if GNU shuf is missing, but shuf is much faster for files with >1000 lines. Example: @@ -2706,7 +2706,7 @@ functions). They are active in all interactive sessions. Synopsis: repo-open [-p|--print] [-r|--root] repo-open --help - Opens the web page for the current repository's `origin` remote in a + Opens the web page for the current repository's origin remote in a browser (via open-url). Deep-links to the current branch when it exists on the remote, falling back to the remote's default branch (main/master) otherwise, and to the current sub-directory when invoked below the repo @@ -2778,7 +2778,7 @@ functions). They are active in all interactive sessions. # 6. DEPENDENCY CATALOG -fish-deps manages these tools. Run `fish-deps` to check status, +`fish-deps` manages these tools. Run `fish-deps` to check status, `fish-deps install` to install missing Required/Recommended ones, or add `--optional`, `--terminals`, or `--all` to also include the Optional and/or Terminal Emulators tiers. @@ -2797,9 +2797,9 @@ Terminal Emulators tiers. | `cargo` | Rust toolchain (via rustup); used by `fish-deps` to install Rust-based tools and to build fish from source. All paths are gated on `type -q cargo` and degrade gracefully. | | `starship` | Cross-shell prompt; loaded via `type -q starship` guard. Without it the Catppuccin nim-style fallback prompt activates. | | `uv` | Python package and project manager (Astral); used by the fish-from-source build path in `fish-deps`. All consumers degrade gracefully without it. | -| `direnv` | Per-directory environment loading; integration is fully guarded with `type -q direnv`. Without it the direnv hook is simply not loaded and auto-venv activates normally. | +| `direnv` | Per-directory environment loading; integration is fully guarded with `type -q direnv`. Without it the `direnv` hook is simply not loaded and auto-venv activates normally. | | `paru` | AUR helper (Arch only; preferred); guarded throughout — non-Arch systems silently skip AUR-specific paths. | -| `yay` | AUR helper (Arch only; fallback to paru); same guards apply. | +| `yay` | AUR helper (Arch only; fallback to `paru`); same guards apply. | | `eza` | Modern `ls` replacement | | `zoxide` | Smart cd with frecency | | `lsd` | `ls` replacement (fallback to `eza`) | @@ -2820,7 +2820,7 @@ matter if you already use that specific tool. Skipped by | `btop` | Modern resource monitor; backs the `top` wrapper (falls back to system `top`). | | `dust` | Disk usage tree (Rust); one of two backends for the `du` wrapper (falls back to system `du`). | | `duf` | Disk usage/free overview; the other backend for the `du` wrapper (falls back to system `du`). | -| `prettyping` | Colorized ping wrapper; backs the `ping` wrapper (falls back to system `ping`). | +| `prettyping` | Colorized `ping` wrapper; backs the `ping` wrapper (falls back to system `ping`). | | `go` | Go toolchain; only used to install `ov` via `go install` (see below), which gets the latest release and doesn't depend on your distro packaging `ov`. Package name varies by distro (`go` on Arch/Homebrew, `golang`/`golang-go` on Debian/Fedora) — install manually if the listed package name doesn't resolve on your system. | | `lazygit` | Terminal git UI; only referenced by the `lg` abbreviation. | | `lazydocker` | Terminal docker UI; backs the `ld` wrapper. | @@ -2886,7 +2886,7 @@ For convenience, a git-ignored `user-dots` symlink in the fish config directory tracks `$__fish_user_dots_path` so the overlay can be browsed from `~/.config/fish/`. It is created if missing and repointed if the path changes. Opt out by setting `__fish_user_dots_symlink` to a falsy value, or toggling -"Dots link" off on the config-settings Paths page — this stops generation and +"Dots link" off on the `config-settings` Paths page — this stops generation and removes any existing link. It only ever manages a symlink and never clobbers a real file or directory at that path. @@ -2896,16 +2896,16 @@ real file or directory at that path. $__fish_user_dots_path/secrets.fish Store API tokens, GPG keys, private credentials here. This file is never -committed. It is sourced by local.fish directly, not by config.fish. +committed. It is sourced by `local.fish` directly, not by `config.fish`. -`local.fish` is sourced at the end of config.fish on every interactive -session, so it and its companion secrets.fish can override anything set +`local.fish` is sourced at the end of `config.fish` on every interactive +session, so it and its companion `secrets.fish` can override anything set earlier. ## Overriding Configuration Variables -Any variable set in local.fish after the main config loads takes effect. +Any variable set in `local.fish` after the main config loads takes effect. Example: to increase the scrollback history limit: # in local.fish @@ -2914,9 +2914,9 @@ Example: to increase the scrollback history limit: ## Fish Universal Variables -Some settings (fzf colors, theme) are stored in fish_variables via +Some settings (`fzf` colors, theme) are stored in `fish_variables` via `set -U`. These are machine-local and git-ignored. Do not commit -fish_variables. +`fish_variables`. ## Opinionated Components (Minimal Mode) @@ -2994,13 +2994,13 @@ Examples: # (erase both to go back to full-flavor defaults) For an interactive alternative to setting these variables by hand, run -config-settings — a full-screen TUI that flips any category (including C5 +`config-settings` — a full-screen TUI that flips any category (including C5 logging) on or off, per session or universally. See its entry in Section 5. NOTE: - Command shadows (rm, cat, ls, ...) react immediately; conf.d-level components (bindings, prompt, abbreviations, hooks) take effect in new shells. - With aliases disabled, rm falls back to bare `command rm` — files are deleted permanently, not trashed. - - Disabled integration commands (spwin, tab, split, hist, logs, upgrade) print an error naming the variable that disabled them. + - Disabled integration commands (`spwin`, `tab`, `split`, `hist`, `logs`, `upgrade`) print an error naming the variable that disabled them. - On CachyOS, the distro fish config's own aliases, history override, and bang-bang bindings are stripped per category as well. ### Sub-categories @@ -3011,7 +3011,7 @@ variable (e.g. `__fish_config_op_aliases_filesystem`). These follow the exact same truthy/falsy/unset cascade one level deeper: an explicit sub-category value overrides the master switch and the parent category's setting, and an unset sub-category inherits from its parent category (which -in turn inherits from `__fish_config_opinionated`). Run config-settings and +in turn inherits from `__fish_config_opinionated`). Run `config-settings` and press Enter on a category row to browse and toggle its sub-categories interactively. See Components Reference for the full sub-category breakdown of every category. @@ -3021,11 +3021,11 @@ full sub-category breakdown of every category. ### Starship -The primary prompt is Starship, initialized by conf.d/starship.fish. -Configure it via ~/.config/starship.toml. +The primary prompt is Starship, initialized by `conf.d/starship.fish`. +Configure it via `~/.config/starship.toml`. -conf.d/starship.fish defines a fish_prompt wrapper that only activates when -starship is in PATH and C3 overrides are enabled (see Opinionated +`conf.d/starship.fish` defines a `fish_prompt` wrapper that only activates when +`starship` is in PATH and C3 overrides are enabled (see Opinionated Components above). It emits OSC 133;A (prompt start) immediately before Starship renders and OSC 133;B (input start) immediately after, placing both markers on the prompt line itself. This allows ov to use them as @@ -3037,7 +3037,7 @@ markers automatically. ### Catppuccin Fallback Prompt When Starship is absent or C3 overrides are disabled, a built-in nim-style -two-line prompt activates from functions/fish_prompt.fish. No external +two-line prompt activates from `functions/fish_prompt.fish`. No external dependencies — fish builtins only. Layout (a dim job line appears between the two rows for each running @@ -3063,7 +3063,7 @@ Elements: ┬─ / ╰─> Connector lines: Catppuccin Green on success, Red on failure -The right prompt (fish_right_prompt.fish) always renders, independently of +The right prompt (`fish_right_prompt.fish`) always renders, independently of which left prompt is active: Segment Shown when @@ -3090,8 +3090,8 @@ the exit-status prefix and timestamp ever appear: ### FZF -FZF is themed to Catppuccin Mocha via FZF_DEFAULT_OPTS, set in -conf.d/theme.fish (opinionated; disabled by `__fish_config_op_overrides`, +FZF is themed to Catppuccin Mocha via `FZF_DEFAULT_OPTS`, set in +`conf.d/theme.fish` (opinionated; disabled by `__fish_config_op_overrides`, see Opinionated Components above). The colors applied: Hex Role Catppuccin name @@ -3106,8 +3106,8 @@ see Opinionated Components above). The colors applied: #F5E0DC Spinner / pointer Rosewater #6C7086 Border Overlay0 -To customize, override FZF_DEFAULT_OPTS in local.fish — it is sourced after -conf.d/theme.fish on every session, so a `set -Ux FZF_DEFAULT_OPTS ...` +To customize, override `FZF_DEFAULT_OPTS` in `local.fish` — it is sourced after +`conf.d/theme.fish` on every session, so a `set -Ux FZF_DEFAULT_OPTS ...` there always wins. ### Catppuccin Mocha Syntax Highlighting @@ -3115,7 +3115,7 @@ there always wins. The Catppuccin Mocha theme ships with this config in themes/ and is applied automatically on first run via `conf.d/first_run.fish` (gated by `__fish_config_op_autoexec`; see Opinionated Components above). Colors are -stored in fish_variables (universal). Three other bundled variants are +stored in `fish_variables` (universal). Three other bundled variants are available in themes/ — Latte, Frappé, and Macchiato. To switch: fish_config theme choose "Catppuccin Latte" @@ -3130,9 +3130,9 @@ category variable. Category Description ────────────────────────────────────────────────────────────────────────── - C1 Command Shadows — Wraps destructive commands (`rm`, `cp`) to be safe by default + C1 Command Shadows — Wraps destructive commands (rm, cp) to be safe by default C2 Startup Side-Effects — Bootstraps Fisher, generates wrappers, auto-activates venvs - C3 Overrides — Overrides `cd`, sets Vi mode, binds `` to `smart_enter` + C3 Overrides — Overrides cd, sets Vi mode, binds to smart_enter C4 Integrations — Kitty/Wezterm integrations, starship hooks, fzf theme C5 Logging and Capture — Session logs, command duration C6 Greeting & First-Run UI — Custom startup banner @@ -3212,7 +3212,7 @@ and the `help config` interception. ### dev-tools -`claude` (AGENTS.md/CLAUDE.md auto-linking) and `edit` (multi-editor +`claude` (`AGENTS.md/CLAUDE.md` auto-linking) and `edit` (multi-editor launcher), plus `agy`. ## C2 — Startup Side-Effects @@ -3233,20 +3233,20 @@ These run automatically without any user action. Disabling user-dots symlink Every startup Links $__fish_config_dir/user-dots to $__fish_user_dots_path -When C2 is disabled: no Fisher install, no theme application, no paru/yay +When C2 is disabled: no Fisher install, no theme application, no `paru`/`yay` wrapper generation, no automatic venv activation, no WakaTime reporting, -no auto-pull (the PWD handler is never registered), and the user-dots +no `auto-pull` (the PWD handler is never registered), and the user-dots convenience symlink is not created. The symlink is git-ignored and only ever managed as a symlink — a real file or directory at that path is left untouched. The symlink has its own opt-out independent of C2: set `__fish_user_dots_symlink` -to a falsy value (or toggle "Dots link" off on the config-settings Paths page) +to a falsy value (or toggle "Dots link" off on the `config-settings` Paths page) to stop generating it and remove any existing link — honoured even when C2 is enabled. Managed by the `__fish_user_dots_link` helper. The first-run completion marker (`__fish_config_first_run_complete`) is still set so the init does not re-run on subsequent shells. Python venv activation fires on every directory change. If a directory uses -direnv (`.envrc` present), direnv takes priority and auto-venv is skipped for +`direnv` (`.envrc` present), `direnv` takes priority and auto-venv is skipped for that directory. Auto-pull fast-forwards opted-in repositories in the background when you cd @@ -3401,59 +3401,59 @@ CAUTION: This configuration is capable of silently recording terminal output and Component What it captures ─────────────────────────────────────────────────────────────────────────── Scrollback capture Terminal session output saved to: - `~/.terminal_history/scrollback_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/scrollback_YYYY-MM-DD_HH-MM-SS.log tmux pane capture Continuous pane stream via pipe-pane, saved to: - `~/.terminal_history/tmux_-w-p_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/tmux_-w-p_YYYY-MM-DD_HH-MM-SS.log zellij pane capture Pane scrollback snapshot on shell exit, saved to: - `~/.terminal_history/zellij_-p_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/zellij_-p_YYYY-MM-DD_HH-MM-SS.log paru wrapper All paru/AUR output captured to: - `~/.terminal_history/paru_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/paru_YYYY-MM-DD_HH-MM-SS.log yay wrapper All yay/AUR output captured to: - `~/.terminal_history/yay_YYYY-MM-DD_HH-MM-SS.log` - Kitty watcher `watcher.py` captures scrollback when Kitty closes + ~/.terminal_history/yay_YYYY-MM-DD_HH-MM-SS.log + Kitty watcher watcher.py captures scrollback when Kitty closes NOTE: **Turning off logging does not delete any existing logs.** They remain in `$SCROLLBACK_HISTORY_DIR` (defaults to: `~/.terminal_history/`) until you remove them manually. -The tmux capture starts automatically when fish launches inside any tmux -pane (`$TMUX` is set). It uses tmux's native pipe-pane to stream all pane +The `tmux` capture starts automatically when fish launches inside any `tmux` +pane (`$TMUX` is set). It uses `tmux`'s native pipe-pane to stream all pane output directly to disk without an intermediate process. Each fish shell session gets its own log file; a new log is created on each shell start (including exec fish and new splits). Before each new log, the oldest `tmux_*.log` files are pruned (by modification time) to keep the total within -`SCROLLBACK_HISTORY_MAX_FILES`, matching the paru/yay wrapper behaviour. +`SCROLLBACK_HISTORY_MAX_FILES`, matching the `paru`/`yay` wrapper behaviour. -The zellij capture works differently: Zellij has no live output-streaming +The `zellij` capture works differently: Zellij has no live output-streaming facility like pipe-pane, so the log is taken as a one-shot snapshot when the shell exits, via `zellij action dump-screen --full --ansi` (the `--ansi` flag preserves color). The dump is captured on the fish process's stdout and written to the log file by fish itself (not via `--path`, which would make the -zellij server write the file). A fish_exit handler (registered whenever +`zellij` server write the file). A `fish_exit` handler (registered whenever `$ZELLIJ` is set) writes the pane's full scrollback and then prunes old `zellij_*.log` files the same way. Because the capture happens at exit, toggling `__fish_config_op_logging` takes effect on the next exit with no restart or sentinel coordination needed — the C5 guard is re-checked when the handler fires. -LIMITATION — zellij capture only fires on a clean shell exit (typing `exit`, -Ctrl-D, or a logout), because that is when the fish_exit handler runs. It does -NOT capture when you close a pane or quit zellij through zellij itself: +LIMITATION — `zellij` capture only fires on a clean shell exit (typing `exit`, +`Ctrl-D`, or a logout), because that is when the `fish_exit` handler runs. It does +NOT capture when you close a pane or quit `zellij` through `zellij` itself: - Closing a pane signals the shell and tears the pane down concurrently, so - even if the handler runs, `dump-screen` may find the pane buffer already + even if the handler runs, dump-screen may find the pane buffer already gone. - - Quitting zellij kills the zellij server, and `dump-screen` needs a live + - Quitting `zellij` kills the `zellij` server, and `dump-screen` needs a live server to read from — there is nothing left to snapshot. -This is a structural difference from tmux, NOT a bug. tmux streams pane output +This is a structural difference from `tmux`, NOT a bug. `tmux` streams pane output to disk continuously via pipe-pane, so whatever was printed is already saved no matter how the pane dies. Zellij can only snapshot, and the only reliable -snapshot point from the shell is a clean exit. To guarantee a zellij pane is -logged, end the session with `exit` or Ctrl-D rather than zellij's close-pane +snapshot point from the shell is a clean exit. To guarantee a `zellij` pane is +logged, end the session with `exit` or `Ctrl-D` rather than `zellij`'s close-pane or quit actions. -The Kitty watcher is managed by the kitty-logging command: it symlinks the +The Kitty watcher is managed by the `kitty-logging` command: it symlinks the watcher (`fish-config-watcher.py`) into the Kitty config directory and wires it into `kitty.conf` via a managed block. Inside Kitty, a non-blocking per-session reminder points first-time users at `kitty-logging install` until @@ -3476,22 +3476,22 @@ start, so it appears without any action on your part. Disabling `__fish_config_op_logging` (or leaving it unset): 1. Creates the sentinel immediately in every open shell. 2. Removes `~/.local/bin/paru` and `~/.local/bin/yay` logging wrappers; - bare `/usr/bin/paru` and `/usr/bin/yay` are used instead. + bare /usr/bin/paru and /usr/bin/yay are used instead. 3. Kitty's `watcher.py` reads the sentinel on each save attempt and skips capture — no Kitty restart required. - 4. smart_exit stops saving scrollback logs. - 5. Stops `tmux pipe-pane` capture in every open fish shell inside tmux. + 4. `smart_exit` stops saving scrollback logs. + 5. Stops `tmux pipe-pane` capture in every open fish shell inside `tmux`. Enabling `__fish_config_op_logging`: 1. Removes the sentinel in every open shell. - 2. Regenerates paru/yay logging wrappers in `~/.local/bin/`. + 2. Regenerates `paru`/`yay` logging wrappers in `~/.local/bin/`. 3. Kitty watcher resumes capture on the next session exit. - 4. Restarts tmux pipe-pane capture in every open fish shell inside tmux. + 4. Restarts `tmux` pipe-pane capture in every open fish shell inside `tmux`. Changes propagate to all running shells through an event handler that fires whenever `__fish_config_op_logging` changes — no shell restart needed. -Note: C3 and C5 compose independently. C3 controls whether the smart_exit +Note: C3 and C5 compose independently. C3 controls whether the `smart_exit` wrapper is active at all; C5 controls only the scrollback-capture block inside it. With C3 disabled, exit is plain builtin exit regardless of C5. @@ -3508,7 +3508,7 @@ Kitty watcher scrollback capture, and `smart_exit`'s logging-guard path. ### multiplexer-capture -tmux `pipe-pane` and zellij `dump-screen` capture. +`tmux` `pipe-pane` and `zellij` `dump-screen` capture. ### pkg-logs @@ -3566,7 +3566,7 @@ commit them. Fisher installs and updates them automatically. ## Sponge History Filtering -Sponge removes failed commands from history and, via conf.d/sponge_privacy.fish, +Sponge removes failed commands from history and, via `conf.d/sponge_privacy.fish`, also filters privacy-sensitive commands through three layers. Detection is heuristic — pattern- and variable-name-based — so this reduces the risk of a credential landing in persistent history; it is not a guarantee that no @@ -3586,14 +3586,14 @@ Commands matching any of these structural signatures are never recorded: - `sshpass`, `docker login -p`, `openssl -passin/-passout` Layer 2 — Dynamic secret values (session globals, refreshed each login): -On the first prompt, after secrets.fish has loaded, the literal values of +On the first prompt, after `secrets.fish` has loaded, the literal values of all exported variables whose names suggest credentials (TOKEN, PASSWORD, -SECRET, API_KEY, etc.) are collected, regex-escaped, and added as a +SECRET, `API_KEY`, etc.) are collected, regex-escaped, and added as a session-scoped overlay. Because globals shadow universals in Fish, the combined list is what sponge sees. Rotating a token takes effect on the next login automatically. -Layer 3 — Per-command filter (sponge_filter_secrets): +Layer 3 — Per-command filter (`sponge_filter_secrets`): Catches credentials in variables exported after login, such as tokens sourced from a project .env file mid-session. @@ -3621,12 +3621,12 @@ their values), add name tokens — via `config-settings` → Sponge, or directly set -U -a __fish_sponge_extra_sensitive ACME_API VAULT_PW Tokens are folded into the Layer 2 name match case-insensitively as substrings, -so ACME_API also covers ACME_API_KEY. (The match uses `--entire` to return the +so `ACME_API` also covers `ACME_API_KEY`. (The match uses `--entire` to return the full variable name, so partial-name tokens dereference the right value.) The `config-settings` Sponge page also surfaces sponge's own tuning variables — -sponge_delay, sponge_successful_exit_codes, sponge_purge_only_on_exit, and -sponge_allow_previously_successful — so they can be changed without typing +`sponge_delay`, `sponge_successful_exit_codes`, `sponge_purge_only_on_exit`, and +`sponge_allow_previously_successful` — so they can be changed without typing variable names. ## Bundled Plugin Functionality @@ -3654,8 +3654,8 @@ The `fish_plugins` file at the config root: - [`jorgebucaran/fisher`](https://github.com/jorgebucaran/fisher) — Plugin manager itself - [`meaningful-ooo/sponge`](https://github.com/meaningful-ooo/sponge) — Remove failed commands from history -To update all Fisher-managed plugins, run `fisher update` or `fish-deps -update` which calls it as its first step. +To update all Fisher-managed plugins, run `fisher update` or +`fish-deps update` which calls it as its first step. --- @@ -3705,14 +3705,14 @@ Or use the interactive TUI — run `config-settings` and navigate to the "Dots Path" row (last row). Press Enter to type a new path, or ← / h to reset to the default. -config.fish sources local.fish from that directory on every interactive -session. local.fish is responsible for sourcing its own secrets.fish: +`config.fish` sources `local.fish` from that directory on every interactive +session. `local.fish` is responsible for sourcing its own `secrets.fish`: $__fish_user_dots_path/ ├── secrets.fish API keys, tokens, passwords, personal identifiers └── local.fish Machine-specific paths, env vars, and sourcing secrets -fish_variables (auto-managed by fish) is excluded from this repo via +`fish_variables` (auto-managed by fish) is excluded from this repo via .gitignore. Do not commit it. ## secrets.fish @@ -3749,9 +3749,9 @@ wrong on any other system. abbr -a dcr 'docker context use my-remote-server' abbr -a dcw 'docker context use work-server' -local.fish is sourced at the end of config.fish with an existence check so +`local.fish` is sourced at the end of `config.fish` with an existence check so the public config works cleanly on any machine without the private repo. -local.fish in turn sources secrets.fish when it exists. +`local.fish` in turn sources `secrets.fish` when it exists. --- @@ -3823,7 +3823,7 @@ For other systems or building from source, see https://fishshell.com. ## Enable or Disable Session Logging Session logging is opt-in: it is off until you turn it on. To enable all -logging and capture (scrollback, tmux/zellij pane logs, AUR helper wrappers, +logging and capture (scrollback, `tmux`/`zellij` pane logs, AUR helper wrappers, Kitty watcher): set -U __fish_config_op_logging on @@ -3843,12 +3843,12 @@ See C5 — Logging and Capture for the full component breakdown. ## Change or Disable the Greeting -This config suppresses the distro greeting (e.g. CachyOS fastfetch) by +This config suppresses the distro greeting (e.g. CachyOS `fastfetch`) by default. To let the distro greeting through: set -U __fish_config_op_greeting off -To set a custom greeting, define fish_greeting in your local.fish: +To set a custom greeting, define `fish_greeting` in your `local.fish`: # in $__fish_user_dots_path/local.fish function fish_greeting @@ -3868,7 +3868,7 @@ Machine-specific config goes in `$__fish_user_dots_path/local.fish` (defaults to `~/.config/.user-dots/fish/local.fish`). Secrets go in `secrets.fish` in the same directory. -If local.fish is not loading, verify the path: +If `local.fish` is not loading, verify the path: echo $__fish_user_dots_path test -f "$__fish_user_dots_path/local.fish"; and echo exists; or echo missing @@ -3900,7 +3900,7 @@ Fix: create a dedicated `conf.d/` file instead of appending to `config.fish`: # ~/.config/fish/conf.d/mytool.fish mytool init fish | source -All existing integrations (starship, zoxide, direnv) already have `conf.d/` +All existing integrations (`starship`, `zoxide`, `direnv`) already have `conf.d/` files. See Return Sentinel for background. ## Missing Dependencies @@ -3937,7 +3937,7 @@ override it in `local.fish` (See Personalization): fish_default_key_bindings This restores Emacs-style bindings without disabling the rest of C3 -(bang-bang, autopair, starship prompt, pager settings, etc.). +(bang-bang, autopair, `starship` prompt, pager settings, etc.). To disable the entire C3 category (Vi mode and all other key/environment overrides): @@ -3953,9 +3953,9 @@ This configuration groups its opinionated behaviors into six categories (C1–C6 Category Description ────────────────────────────────────────────────────────────────────────── - C1 Command Shadows — Wraps destructive commands (`rm`, `cp`) to be safe by default + C1 Command Shadows — Wraps destructive commands (rm, cp) to be safe by default C2 Startup Side-Effects — Bootstraps Fisher, generates wrappers, auto-activates venvs - C3 Overrides — Overrides `cd`, sets Vi mode, binds `` to `smart_enter` + C3 Overrides — Overrides cd, sets Vi mode, binds to smart_enter C4 Integrations — Kitty/Wezterm integrations, starship hooks, fzf theme C5 Logging and Capture — Session logs, command duration C6 Greeting & First-Run UI — Custom startup banner @@ -4012,7 +4012,7 @@ its search box to jump straight to what you need. help config --man help config pkg --man -Opens the compiled docs/fish-config.1 directly via man -l, bypassing +Opens the compiled `docs/fish-config.1` directly via man `-l`, bypassing the pager fallback chain. If a section keyword is given, the pager opens at the nearest matching heading. The symlink is created once on first run (like an install step) and MANPATH is set each session, enabling @@ -4020,7 +4020,7 @@ the standard invocation: man fish-config -NOTE: fish-config (hyphen) is this config's man page. fish_config +NOTE: fish-config (hyphen) is this config's man page. `fish_config` (underscore) is fish's built-in browser-based configuration tool — a completely separate command. Do not mix them up. diff --git a/docs/manual/08-components-reference/05-c5-logging-and-capture.md b/docs/manual/08-components-reference/05-c5-logging-and-capture.md index 4867c1e..3969c99 100644 --- a/docs/manual/08-components-reference/05-c5-logging-and-capture.md +++ b/docs/manual/08-components-reference/05-c5-logging-and-capture.md @@ -18,16 +18,16 @@ CAUTION: This configuration is capable of silently recording terminal output and Component What it captures ─────────────────────────────────────────────────────────────────────────── Scrollback capture Terminal session output saved to: - `~/.terminal_history/scrollback_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/scrollback_YYYY-MM-DD_HH-MM-SS.log tmux pane capture Continuous pane stream via pipe-pane, saved to: - `~/.terminal_history/tmux_-w-p_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/tmux_-w-p_YYYY-MM-DD_HH-MM-SS.log zellij pane capture Pane scrollback snapshot on shell exit, saved to: - `~/.terminal_history/zellij_-p_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/zellij_-p_YYYY-MM-DD_HH-MM-SS.log paru wrapper All paru/AUR output captured to: - `~/.terminal_history/paru_YYYY-MM-DD_HH-MM-SS.log` + ~/.terminal_history/paru_YYYY-MM-DD_HH-MM-SS.log yay wrapper All yay/AUR output captured to: - `~/.terminal_history/yay_YYYY-MM-DD_HH-MM-SS.log` - Kitty watcher `watcher.py` captures scrollback when Kitty closes + ~/.terminal_history/yay_YYYY-MM-DD_HH-MM-SS.log + Kitty watcher watcher.py captures scrollback when Kitty closes NOTE: **Turning off logging does not delete any existing logs.** They remain in `$SCROLLBACK_HISTORY_DIR` (defaults to: `~/.terminal_history/`) @@ -58,7 +58,7 @@ Ctrl-D, or a logout), because that is when the fish_exit handler runs. It does NOT capture when you close a pane or quit zellij through zellij itself: - Closing a pane signals the shell and tears the pane down concurrently, so - even if the handler runs, `dump-screen` may find the pane buffer already + even if the handler runs, dump-screen may find the pane buffer already gone. - Quitting zellij kills the zellij server, and `dump-screen` needs a live server to read from — there is nothing left to snapshot. @@ -93,7 +93,7 @@ start, so it appears without any action on your part. Disabling `__fish_config_op_logging` (or leaving it unset): 1. Creates the sentinel immediately in every open shell. 2. Removes `~/.local/bin/paru` and `~/.local/bin/yay` logging wrappers; - bare `/usr/bin/paru` and `/usr/bin/yay` are used instead. + bare /usr/bin/paru and /usr/bin/yay are used instead. 3. Kitty's `watcher.py` reads the sentinel on each save attempt and skips capture — no Kitty restart required. 4. smart_exit stops saving scrollback logs. diff --git a/docs/manual/08-components-reference/index.md b/docs/manual/08-components-reference/index.md index a433194..d0be902 100644 --- a/docs/manual/08-components-reference/index.md +++ b/docs/manual/08-components-reference/index.md @@ -12,9 +12,9 @@ category variable. Category Description ────────────────────────────────────────────────────────────────────────── - C1 [Command Shadows](/08-components-reference/01-c1-command-shadows/) — Wraps destructive commands (`rm`, `cp`) to be safe by default + C1 [Command Shadows](/08-components-reference/01-c1-command-shadows/) — Wraps destructive commands (rm, cp) to be safe by default C2 [Startup Side-Effects](/08-components-reference/02-c2-startup-side-effects/) — Bootstraps Fisher, generates wrappers, auto-activates venvs - C3 [Overrides](/08-components-reference/03-c3-key-and-environment-overrides/) — Overrides `cd`, sets Vi mode, binds `` to `smart_enter` + C3 [Overrides](/08-components-reference/03-c3-key-and-environment-overrides/) — Overrides cd, sets Vi mode, binds to smart_enter C4 [Integrations](/08-components-reference/04-c4-terminal-and-tool-integration/) — Kitty/Wezterm integrations, starship hooks, fzf theme C5 [Logging and Capture](/08-components-reference/05-c5-logging-and-capture/) — Session logs, command duration C6 [Greeting & First-Run UI](/08-components-reference/06-c6-greeting-and-first-run-ui/) — Custom startup banner diff --git a/docs/manual/09-fisher-plugins.md b/docs/manual/09-fisher-plugins.md index 3457453..e19d761 100644 --- a/docs/manual/09-fisher-plugins.md +++ b/docs/manual/09-fisher-plugins.md @@ -120,7 +120,7 @@ The `fish_plugins` file at the config root: - [`jorgebucaran/fisher`](https://github.com/jorgebucaran/fisher) — Plugin manager itself - [`meaningful-ooo/sponge`](https://github.com/meaningful-ooo/sponge) — Remove failed commands from history -To update all Fisher-managed plugins, run `fisher update` or `fish-deps -update` which calls it as its first step. +To update all Fisher-managed plugins, run `fisher update` or +`fish-deps update` which calls it as its first step. --- diff --git a/docs/manual/12-troubleshooting.md b/docs/manual/12-troubleshooting.md index a823530..5439136 100644 --- a/docs/manual/12-troubleshooting.md +++ b/docs/manual/12-troubleshooting.md @@ -208,9 +208,9 @@ This configuration groups its opinionated behaviors into six categories (C1–C6 Category Description ────────────────────────────────────────────────────────────────────────── - C1 [Command Shadows](/08-components-reference/01-c1-command-shadows/) — Wraps destructive commands (`rm`, `cp`) to be safe by default + C1 [Command Shadows](/08-components-reference/01-c1-command-shadows/) — Wraps destructive commands (rm, cp) to be safe by default C2 [Startup Side-Effects](/08-components-reference/02-c2-startup-side-effects/) — Bootstraps Fisher, generates wrappers, auto-activates venvs - C3 [Overrides](/08-components-reference/03-c3-key-and-environment-overrides/) — Overrides `cd`, sets Vi mode, binds `` to `smart_enter` + C3 [Overrides](/08-components-reference/03-c3-key-and-environment-overrides/) — Overrides cd, sets Vi mode, binds to smart_enter C4 [Integrations](/08-components-reference/04-c4-terminal-and-tool-integration/) — Kitty/Wezterm integrations, starship hooks, fzf theme C5 [Logging and Capture](/08-components-reference/05-c5-logging-and-capture/) — Session logs, command duration C6 [Greeting & First-Run UI](/08-components-reference/06-c6-greeting-and-first-run-ui/) — Custom startup banner diff --git a/docs/site/README.md b/docs/site/README.md index a7d25c6..5fcee38 100644 --- a/docs/site/README.md +++ b/docs/site/README.md @@ -25,8 +25,13 @@ first if you've touched a header or a manual page. Function headers are read as plain text (by `config-help`, by `funcsave`, by anyone opening the `.fish` file), so they're authored without backticks — `-a/--all`, not `` `-a`/`--all` ``. `docs/codespans.py` puts the -backticks on at render time, as the last step of `prettify()`, so only the -site sees them. +backticks on at render time, as the last step of `prettify()`. + +`build_concat()` runs the same pass, so the man page and `config-help` +mark code the way the site does rather than only where the SSOT happened +to backtick something by hand. `config-help` then renders those spans +bold and drops the delimiters, since a terminal pager would otherwise +show them as literal punctuation. It recognises flags, `$vars`, `SCREAMING_SNAKE` env vars, snake_case identifiers (`__fish_config_op_aliases`, `fish_greeting`), paths and @@ -41,10 +46,14 @@ Names that also read as English (`find`, `top`, `screen`) are listed in where position already proves they're a command. Add to that list rather than removing a rule if a wrap ever reads wrong. -Fenced blocks, existing code spans, headings, link targets, URLs, -component markup, and `` bodies are never touched. Leaving a -token alone is always the safe outcome, so every rule bails out when it -isn't sure. +Fenced blocks, indented blocks, existing code spans, headings, link +targets, URLs, component markup, and `` bodies are never +touched. Leaving a token alone is always the safe outcome, so every rule +bails out when it isn't sure. + +Indented blocks matter only to the concat — `prettify()` has already +fenced them by the time the site is rendered — but there they are the +table of contents and every section 5 entry, which must stay verbatim. ## llms.txt diff --git a/docs/verify-manual.py b/docs/verify-manual.py index a5db59f..f89adb9 100644 --- a/docs/verify-manual.py +++ b/docs/verify-manual.py @@ -1401,13 +1401,79 @@ def test_codespans_vocabulary_comes_from_the_deps_catalog(): assert "find" in vocab.full, "…but it still counts as a command-line opener" -def test_codespans_is_site_only(): - """The concat (man page, config-help) keeps the plain-text form.""" +def test_codespans_leaves_indented_code_blocks_alone(): + """A four-space block is code, whatever it happens to contain. + + The concat keeps the indented form pandoc wants, so unlike the site + this pass meets real indented blocks -- the table of contents among + them, which is nothing but a list of command names. + """ + body = "\n".join( + [ + "Pick a viewer:", + "", + " 1. ov + bat section navigation", + " 2. less plain text with --jump", + "", + "Then run config-help.", + ] + ) + got = _spans(body).split("\n") + assert got[2] == " 1. ov + bat section navigation", got[2] + assert got[3] == " 2. less plain text with --jump", got[3] + assert got[5] == "Then run `config-help`.", "prose after the block was skipped" + + +def test_codespans_reach_the_man_page_pipeline(): + """Prose is marked identically wherever it is rendered. + + `build_concat` runs the same pass `build_site` does, so a token the + site typesets as code is typeset as code in the man page and + `config-help` too, instead of only where the SSOT hand-wrote a + backtick. + """ + import build_manual + + manual = Path(__file__).parent / "manual" + text = build_manual.build_concat(manual) + assert "`local.fish`" in text, "prose code spans never reached the concat" + assert "`tmux`" in text, "a vocabulary command was not wrapped in the concat" + + +def test_concat_code_spans_never_straddle_a_line(): + """`config-help` pairs backticks one line at a time. + + Its `string replace` filters run per line, so a span split across a + line break -- ``run `fish-deps\\nupdate` `` -- leaves an unpaired + backtick the pager then shows literally. Markdown is happy to wrap + one, so nothing else catches this. + """ import build_manual text = build_manual.build_concat(Path(__file__).parent / "manual") - assert "-r/--resume" in text, "sanity: the bare flag-pair form is what's authored" - assert "`-r`/`--resume`" not in text, "code spans leaked into the man-page pipeline" + odd = [ + (n, line) + for n, line in enumerate(text.split("\n"), 1) + if line.count("`") % 2 + ] + assert not odd, f"unpaired backtick, span wraps a line: {odd[:3]}" + + +def test_concat_section_five_stays_verbatim(): + """Section 5's entries are indented blocks, not prose. + + They are generated from the `functions/*.fish` headers and pandoc sets + them verbatim, so a backtick there would be a literal character on the + page rather than markup. + """ + import build_manual + + text = build_manual.build_concat(Path(__file__).parent / "manual") + body = text.split("\n# 5. ", 1)[1].split("\n# 6. ", 1)[0] + offenders = [ + line for line in body.split("\n") if line.startswith(" ") and "`" in line + ] + assert not offenders, f"backticks inside verbatim entries: {offenders[:3]}" TESTS = [v for k, v in sorted(globals().items()) if k.startswith("test_")] diff --git a/functions/__config_settings_get_val.fish b/functions/__config_settings_get_val.fish index 6caac00..2670415 100644 --- a/functions/__config_settings_get_val.fish +++ b/functions/__config_settings_get_val.fish @@ -6,7 +6,7 @@ # # DESCRIPTION # Returns the current value of a named variable in the specified scope by -# parsing `set --show` output. Outputs "on", "off", or "DEFAULT" (when +# parsing set --show output. Outputs "on", "off", or "DEFAULT" (when # the variable is not set in that scope). Scope "session" maps to "global" # in fish's internal terminology. # diff --git a/functions/__config_settings_read_key.fish b/functions/__config_settings_read_key.fish index abab5ef..e0903b1 100644 --- a/functions/__config_settings_read_key.fish +++ b/functions/__config_settings_read_key.fish @@ -7,11 +7,11 @@ # DESCRIPTION # Reads a single keypress directly from the controlling terminal in raw # mode and echoes a normalized token naming the key. Bypasses fish's -# `read` builtin, whose interactive line editor swallows Tab and arrow -# keys (and prints a `read> ` prompt) — none of which is usable for a TUI. +# read builtin, whose interactive line editor swallows Tab and arrow +# keys (and prints a "read> " prompt) — none of which is usable for a TUI. # # The terminal is put into raw, no-echo mode with a 0.1s inter-byte timer -# (`stty raw -echo min 1 time 1`) so a multi-byte escape sequence (e.g. +# (stty raw -echo min 1 time 1) so a multi-byte escape sequence (e.g. # an arrow key, ESC [ A) is captured in one read while a lone key returns # promptly. Original terminal settings are always restored before return. # diff --git a/functions/__fish_config_op_enabled.fish b/functions/__fish_config_op_enabled.fish index ec877e2..eb59911 100644 --- a/functions/__fish_config_op_enabled.fish +++ b/functions/__fish_config_op_enabled.fish @@ -17,11 +17,11 @@ # Looks up ":" (site defaults to the empty/unnamed site) # in the generated component registry. No registry entry (unclassified, # or a doc header with no # COMPONENT section) resolves to enabled -- -# the same fail-open default as an explicit `always/on` tag, so +# the same fail-open default as an explicit always/on tag, so # user-authored and third-party functions that never call this guard in # the first place are unaffected, and one that somehow does is never -# silently broken by a missing header. A found `always/off` tag -# disables unconditionally; a found `always/on` tag enables +# silently broken by a missing header. A found always/off tag +# disables unconditionally; a found always/on tag enables # unconditionally, short-circuiting before any other tagged # sub-category is evaluated. Otherwise every tagged sub-category must # pass the cascade (AND semantics). diff --git a/functions/__fish_user_dots_link.fish b/functions/__fish_user_dots_link.fish index f02e583..5bec228 100644 --- a/functions/__fish_user_dots_link.fish +++ b/functions/__fish_user_dots_link.fish @@ -8,7 +8,7 @@ # __fish_user_dots_link # # DESCRIPTION -# Manages the git-ignored `user-dots` convenience symlink in the fish config +# Manages the git-ignored user-dots convenience symlink in the fish config # directory ($__fish_config_dir/user-dots), pointing it at the resolved # $__fish_user_dots_path so the private overlay can be browsed from # ~/.config/fish/. diff --git a/functions/__jobrunner_sessions.fish b/functions/__jobrunner_sessions.fish index 735a7a3..b3958fd 100644 --- a/functions/__jobrunner_sessions.fish +++ b/functions/__jobrunner_sessions.fish @@ -5,7 +5,7 @@ # __jobrunner_sessions [] # # DESCRIPTION -# Parses `tmux list-sessions` or `screen -ls` into machine-readable rows, +# Parses tmux list-sessions or screen -ls into machine-readable rows, # one per active session: name, PID, state, and start time separated by tabs. # Shared by jobrunner and its completions so both agree on what a session is # named. Prints nothing when no sessions exist. diff --git a/functions/__kitty_logging_has_watcher.fish b/functions/__kitty_logging_has_watcher.fish index a49d0eb..3455eee 100644 --- a/functions/__kitty_logging_has_watcher.fish +++ b/functions/__kitty_logging_has_watcher.fish @@ -6,7 +6,7 @@ # # DESCRIPTION # Succeeds (returns 0) when the top-level kitty.conf contains an active -# (non-commented) `watcher` directive — whether the fish-config managed one or +# (non-commented) watcher directive — whether the fish-config managed one or # a user's own. Used to suppress the setup reminder and to inform status. # # EXIT STATUS diff --git a/functions/_agents_init_ensure_gitignore.fish b/functions/_agents_init_ensure_gitignore.fish index 3df0fd3..45d4c9f 100644 --- a/functions/_agents_init_ensure_gitignore.fish +++ b/functions/_agents_init_ensure_gitignore.fish @@ -6,9 +6,9 @@ # # DESCRIPTION # Appends any patterns not already covered by the project's .gitignore. -# Uses `git check-ignore` for accurate rule matching (catches wildcards +# Uses git check-ignore for accurate rule matching (catches wildcards # and parent-dir globs). Falls back to a plain string search when the -# root is not a git repository. Leading `/` is stripped from each pattern +# root is not a git repository. Leading / is stripped from each pattern # before the path-based check so root-anchored patterns (e.g. /AGENTS.md) # are matched correctly. # diff --git a/functions/_agents_init_install_tools.fish b/functions/_agents_init_install_tools.fish index 3c544af..5491971 100644 --- a/functions/_agents_init_install_tools.fish +++ b/functions/_agents_init_install_tools.fish @@ -7,7 +7,7 @@ # DESCRIPTION # Copies the canonical version-bump script and git hook shims from # fish-config's scripts/agents-tools/ into /.agents-tools/, -# refreshing them when the shipped `agents-tools-version:` marker is newer +# refreshing them when the shipped agents-tools-version: marker is newer # than the installed copy. Files are made executable. Idempotent: prints # nothing when the installed tooling is already current, or a short summary # line when it installed or updated the tooling. diff --git a/functions/_fish_deps_catalog.fish b/functions/_fish_deps_catalog.fish index 41aa526..d466fbe 100644 --- a/functions/_fish_deps_catalog.fish +++ b/functions/_fish_deps_catalog.fish @@ -18,13 +18,13 @@ # conveniences (e.g. backs one wrapper # function) that only matter if you already # use that specific tool. Skipped by -# `fish-deps install`/`sync` unless -# `--optional` (or `--all`) is passed. +# fish-deps install/sync unless +# --optional (or --all) is passed. # term Terminal Emulators — GPU-accelerated terminal emulators # (kitty, wezterm) that only matter if one # of them is your actual terminal. Skipped -# by `fish-deps install`/`sync` unless -# `--terminals` (or `--all`) is passed. +# by fish-deps install/sync unless +# --terminals (or --all) is passed. # int Integrations — opt-in third-party services requiring # their own account/setup (wakatime, # tailscale). diff --git a/functions/_fzf_preview_media.fish b/functions/_fzf_preview_media.fish index 375cdfd..76abc4f 100644 --- a/functions/_fzf_preview_media.fish +++ b/functions/_fzf_preview_media.fish @@ -9,7 +9,7 @@ # by a desktop file manager (Dolphin, Nautilus, GNOME Videos, ...) in the # freedesktop thumbnail cache and renders it via _fzf_preview_image if # found. Otherwise falls back to ffprobe-formatted metadata (duration, -# codec, resolution, tags) when ffprobe is installed, or plain `file` +# codec, resolution, tags) when ffprobe is installed, or plain file # output as a last resort. Neither the thumbnail cache lookup nor ffprobe # are tracked in fish-deps: both are best-effort, matching how the # image-preview tool chain (kitten/chafa/viu/timg) is already handled. diff --git a/functions/_prune_terminal_logs.fish b/functions/_prune_terminal_logs.fish index f5c4b41..5a8b546 100644 --- a/functions/_prune_terminal_logs.fish +++ b/functions/_prune_terminal_logs.fish @@ -11,9 +11,9 @@ # modification time, so the most recently written logs are kept — actively # appended logs (e.g. a tmux pipe-pane stream) survive. # -# Uses `command ls`/`command rm` to bypass the C1 shadows: the bare `ls` is +# Uses command ls/command rm to bypass the C1 shadows: the bare ls is # the eza wrapper, which injects OSC-8 hyperlink escapes into paths, and the -# bare `rm` is the trash wrapper. The glob is expanded via `set` first so a +# bare rm is the trash wrapper. The glob is expanded via set first so a # no-match (empty dir / first run) yields an empty list instead of a hard # "No matches for wildcard" error. # diff --git a/functions/_zellij_dump_log.fish b/functions/_zellij_dump_log.fish index 46ab9bb..0808b5d 100644 --- a/functions/_zellij_dump_log.fish +++ b/functions/_zellij_dump_log.fish @@ -11,7 +11,7 @@ # Captures the current Zellij pane's scrollback to a timestamped log in # SCROLLBACK_HISTORY_DIR (default ~/.terminal_history). Zellij has no live # output-streaming facility like tmux's pipe-pane, so this performs a one-shot -# `zellij action dump-screen --full` — intended to run on shell exit. Old +# zellij action dump-screen --full — intended to run on shell exit. Old # zellij_*.log files are pruned via _prune_terminal_logs to stay within # SCROLLBACK_HISTORY_MAX_FILES. # diff --git a/functions/config-help.fish b/functions/config-help.fish index 562874e..fa75aed 100644 --- a/functions/config-help.fish +++ b/functions/config-help.fish @@ -22,7 +22,7 @@ # in the default browser via xdg-open — deep links to a section aren't # supported there, so if a keyword is given a note points you to the site's # search box instead. Pass --man / -m to open the compiled man page -# (docs/fish-config.1) via `man -l`; if a section keyword is given, the +# (docs/fish-config.1) via man -l; if a section keyword is given, the # pager opens at the nearest match. Pass --help or -h for usage and the # navigation key reference. # @@ -52,9 +52,9 @@ # config-help pkg --man # # NOTES -# The preferred invocation is `help config [...]` — this function is +# The preferred invocation is help config [...] — this function is # registered as a handler in the help wrapper so that syntax works -# transparently. Direct `config-help` calls are also valid. +# transparently. Direct config-help calls are also valid. function config-help --description 'Open the offline fish shell configuration manual' set -l doc_file "$__fish_config_dir/docs/fish-config.md" set -l idx_file "$__fish_config_dir/docs/fish-config.index" @@ -247,6 +247,22 @@ function config-help --description 'Open the offline fish shell configuration ma set -l nav_hint \ " \033[2mNAVIGATION: [ Space=next section ^=prev Alt+u=sections /=search q=quit ]\033[0m" + # ── Inline code spans ──────────────────────────────────────── + # The document carries backticks for pandoc and the docs site, but + # nothing in this chain consumes them: bat dims the delimiters and + # leaves the content the same colour as the surrounding prose, so + # they arrive as literal punctuation. Render each span bold instead. + # + # Two forms are matched. After bat, every backtick carries its own + # SGR sequence, and a fence survives because it puts three of them + # inside one sequence. On raw Markdown a fence survives because it + # offers no non-backtick content to capture. Both substitutions are + # line-preserving, so the tail-slice below still lands on the + # requested section. + set -l span_ansi '\e\[[0-9;]*m`\e\[0m(.*?)\e\[[0-9;]*m`\e\[0m' + set -l span_raw '`([^`]+)`' + set -l span_bold (printf '\e[1m$1\e[0m') + # ── Viewer fallback chain ──────────────────────────────────── # When jumping to a section, slice the file from start_line so ov # opens with that section at the top without needing --pattern. @@ -261,12 +277,14 @@ function config-help --description 'Open the offline fish shell configuration ma begin printf "$nav_hint\n" bat --color=always --style=plain --language=markdown "$doc_file" \ - | tail -n +$start_line + | tail -n +$start_line \ + | string replace -ra $span_ansi $span_bold end | ov $ov_args else begin printf "$nav_hint\n" - bat --color=always --style=plain --language=markdown "$doc_file" + bat --color=always --style=plain --language=markdown "$doc_file" \ + | string replace -ra $span_ansi $span_bold end | ov $ov_args end @@ -279,12 +297,13 @@ function config-help --description 'Open the offline fish shell configuration ma if test $start_line -gt 1 begin printf "$nav_hint\n" - tail -n +$start_line "$doc_file" + tail -n +$start_line "$doc_file" \ + | string replace -ra $span_raw $span_bold end | ov $ov_args else begin printf "$nav_hint\n" - cat "$doc_file" + string replace -ra $span_raw $span_bold <"$doc_file" end | ov $ov_args end @@ -295,16 +314,23 @@ function config-help --description 'Open the offline fish shell configuration ma echo "note: bat pager — use / to search for your section" >&2 set_color normal end - bat --language=markdown --paging=always "$doc_file" + # bat owns the pager here, so the spans are flattened on the way + # in rather than styled on the way out — bat would escape any + # SGR sequence handed to it as input. + string replace -ra $span_raw '$1' <"$doc_file" \ + | bat --language=markdown --paging=always # Pre-compiled man page (generated by CI after merge). + # pandoc consumed the backticks when it built this, so there is + # nothing to strip. else if test -f "$man_file" man -l "$man_file" else if type -q less - less +"$start_line" "$doc_file" + string replace -ra $span_raw $span_bold <"$doc_file" \ + | less -R +"$start_line" else - cat "$doc_file" + string replace -ra $span_raw $span_bold <"$doc_file" end end diff --git a/functions/config-update.fish b/functions/config-update.fish index 37bf5f4..1d3fc8a 100644 --- a/functions/config-update.fish +++ b/functions/config-update.fish @@ -11,7 +11,7 @@ # Pulls the latest fish shell configuration from the upstream repository # into ~/.config/fish. Git output is suppressed; status is reported # through colored messages. After a successful pull the function prints a -# short summary of changed files; run `exec fish` to reload the shell. +# short summary of changed files; run exec fish to reload the shell. # # ARGUMENTS # -h, --help Show this help message and exit diff --git a/functions/fish-deps.fish b/functions/fish-deps.fish index b0860e6..bf9839e 100644 --- a/functions/fish-deps.fish +++ b/functions/fish-deps.fish @@ -29,10 +29,10 @@ # Optional btop, dust, duf, prettyping, go, lazygit, # lazydocker, docker, yt-dlp, screen — single-purpose # wrapper conveniences that only matter if you -# already use that tool; skipped by `install`/`sync` +# already use that tool; skipped by install/sync # unless --optional (or --all) is passed # Terminal Emulators kitty, wezterm — only matter if one of them is -# your actual terminal; skipped by `install`/`sync` +# your actual terminal; skipped by install/sync # unless --terminals (or --all) is passed # Integrations wakatime, tailscale # diff --git a/functions/jobrunner.fish b/functions/jobrunner.fish index 33951d0..1fffdf6 100644 --- a/functions/jobrunner.fish +++ b/functions/jobrunner.fish @@ -15,13 +15,13 @@ # Runs, lists, inspects, re-attaches to, and terminates named background # jobs using tmux or GNU screen as the process engine. Unlike bkg and # detach, which discard output, a jobrunner job keeps a live terminal you -# can return to later — it survives closing the shell, and `attach` +# can return to later — it survives closing the shell, and attach # restores it in any subsequent session. # Run and manage named background jobs. Jobs are detached from the shell # and backed by tmux (preferred) or GNU screen. # -# If the job name is omitted when starting a new job (e.g. `jobrunner sleep 1`), -# a memorable, random name (like `sleepy-badger`) will be generated. +# If the job name is omitted when starting a new job (e.g. jobrunner sleep 1), +# a memorable, random name (like sleepy-badger) will be generated. # # SUBCOMMANDS # run, -r, --run [-n ] Start a new background job @@ -49,7 +49,7 @@ # Detach from an attached job with Ctrl-A then D; the job keeps running. # Commands are executed directly rather than through a shell, so pipes and # redirections must be wrapped explicitly, e.g. -# `jobrunner run sync fish -c 'a | b'`. +# jobrunner run sync fish -c 'a | b'. function jobrunner --description 'Manage detached background jobs with tmux or GNU screen' set -l c_head (set_color --bold cyan) set -l c_cmd (set_color --bold) diff --git a/functions/kitty-logging.fish b/functions/kitty-logging.fish index 3ab50ae..dbff8d9 100644 --- a/functions/kitty-logging.fish +++ b/functions/kitty-logging.fish @@ -12,11 +12,11 @@ # # DESCRIPTION # Manages the fish-config Kitty scrollback watcher that powers C5 logging. -# `install` symlinks the canonical watcher into the Kitty config dir (so it +# install symlinks the canonical watcher into the Kitty config dir (so it # always tracks the source) and wires it into kitty.conf via a # sentinel-marked managed block, commenting out any conflicting active -# watcher line to avoid double-capture. `uninstall` reverses it. `status` -# reports wiring, installed watcher version, and C5 logging state. `dismiss` +# watcher line to avoid double-capture. uninstall reverses it. status +# reports wiring, installed watcher version, and C5 logging state. dismiss # silences the per-session setup reminder. # # Runtime capture stays governed by the C5 .logging_disabled sentinel, so diff --git a/functions/mv.fish b/functions/mv.fish index e8e4b8b..82efcf1 100644 --- a/functions/mv.fish +++ b/functions/mv.fish @@ -13,7 +13,7 @@ # DESCRIPTION # Wraps mv to automatically collapse nested directories of the same name. # When extracting archives results in redundant structures (e.g., -# themes/themes/), calling `mv themes/themes themes` will gracefully +# themes/themes/), calling mv themes/themes themes will gracefully # move the inner contents up one level and remove the empty outer shell. # # Opinionated component (C1): when disabled via __fish_config_op_aliases, diff --git a/functions/qc.fish b/functions/qc.fish index 9cb8a55..f598ec0 100644 --- a/functions/qc.fish +++ b/functions/qc.fish @@ -15,7 +15,7 @@ # scripts/cli-agent.md to $XDG_CONFIG_HOME/aichat/roles/cli.md. Inherits # every aichat flag and tab completion (--wraps aichat); passing --role/-r # overrides the default role, so qc forwards to aichat unchanged. The -# function is only defined when aichat is installed. Run `qc --help` for +# function is only defined when aichat is installed. Run qc --help for # aichat's full flag reference with the command name rewritten to qc. # # ARGUMENTS diff --git a/functions/rand_string.fish b/functions/rand_string.fish index b150c02..c5b0312 100644 --- a/functions/rand_string.fish +++ b/functions/rand_string.fish @@ -10,9 +10,9 @@ # DESCRIPTION # Generates a random, memorable string using a sequence of specified word # categories and formatting modifiers. Words are pulled from curated -# plain-text databases bundled in `data/words/`. +# plain-text databases bundled in data/words/. # -# Modifiers like `--separator` and `--case` are evaluated sequentially and +# Modifiers like --separator and --case are evaluated sequentially and # apply only to the components that follow them. # # Supported Components: @@ -35,7 +35,7 @@ # rand_string literal=TEST --separator=underscore verb noun # # NOTES -# Falls back to `random choice` if GNU `shuf` is missing, but `shuf` is +# Falls back to random choice if GNU shuf is missing, but shuf is # much faster for files with >1000 lines. function rand_string --description 'Generate random, memorable strings from curated word databases' set -l c_head (set_color --bold cyan) diff --git a/functions/repo-open.fish b/functions/repo-open.fish index 9cb6b7e..60eee02 100644 --- a/functions/repo-open.fish +++ b/functions/repo-open.fish @@ -12,7 +12,7 @@ # repo-open --help # # DESCRIPTION -# Opens the web page for the current repository's `origin` remote in a +# Opens the web page for the current repository's origin remote in a # browser (via open-url). Deep-links to the current branch when it exists # on the remote, falling back to the remote's default branch (main/master) # otherwise, and to the current sub-directory when invoked below the repo diff --git a/functions/smart_exit.fish b/functions/smart_exit.fish index 6b509e7..4d34c61 100644 --- a/functions/smart_exit.fish +++ b/functions/smart_exit.fish @@ -31,7 +31,7 @@ # # NOTES # The exit builtin is wired to smart_exit for interactive sessions. Typing -# `exit` or Ctrl+D behaves identically to calling smart_exit directly. +# exit or Ctrl+D behaves identically to calling smart_exit directly. function smart_exit --description 'Capture colorized scrollback before exiting, with pruning and safe overrides' # Opinionated guard (C3): exit plainly when overrides are disabled. # This composes with Task #4's __fish_config_enable_logging, which will