feat(help): render code spans in the pager instead of printing backticks #121

Merged
rootiest merged 5 commits from feat/consistent-code-spans-in-concat into main 2026-09-01 02:11:27 +00:00
Owner

config-help showed 1236 literal backticks across the manual. The fix turned out to belong at the viewer, not in the SSOT.

Why not strip the SSOT

Measured it first: stripping every authored span and re-running the generator recovers only 306 of 510 (60%). The 204 losses are structural, not a gap to close —

Class Count Recoverable
Ambiguous commands in prose (ov, less, cd, rm) 48 No — excluded by design
Multi-word command lines (fish-deps install, set -U) 40 No
Literals/placeholders (!!, always, on/off, __fish_config_op_aliases_<slug>) 69 No
Bare env vars, kebab names, script(1), numbers 30 Partly

Teaching codespans.py to wrap ov or set -U in running prose is precisely the over-wrapping it is built to avoid, and stripping would have flattened the man page too.

What this does instead

bat dims the backticks and gives span content the same colour as the surrounding prose — the delimiters carried no information at all. So each span is rendered bold and the delimiters dropped, in every branch of the viewer chain (ov+bat, ov alone, bat alone, less, cat; man -l needs nothing). Both substitutions are line-preserving, so the tail-slice that opens the pager on a section still lands on it.

With the viewer no longer showing them, the SSOT is free to keep its markup, and build_concat() now runs the same codespans pass the site does — so a token is typeset the same way wherever it is read (549 → 662 spans in the concat, and the man page gains code formatting it never had).

Cleanups this surfaced

  • 22 function doc-headers carried 41 hand-written spans, against the rule CONTRIBUTING already states. They reached config-help and the man page as literal characters inside a verbatim block.
  • 12 lines had backticks inside four-space blocks, which are verbatim in every renderer — literal characters on the site too, inside a code fence.
  • One span wrapped a line break, so the pager showed both halves.
  • codespans now protects indented blocks. Without it the table of contents came out with ov, bat, less and cat wrapped inside a code block.

Verification

  • verify-manual.py 74/74, tests/run-tests.fish 220/220 lint + 10/10
  • Site output confirmed byte-identical under the codespans change alone
  • End-to-end: 1236 → 0 literal backticks in every viewer path, line counts preserved
  • test_codespans_is_site_only asserted the opposite guarantee and was passing only because its example sat inside a newly-protected block; replaced by tests for what is now true

Trade-off: the site loses 22 spans (install/sync and similar ambiguous words the detector won't wrap on sight) as the cost of holding the plain-text header rule. Easy to hand back if you'd rather keep them.

The man page is left for CI to regenerate — pandoc isn't needed to build the concat.

`config-help` showed **1236 literal backticks** across the manual. The fix turned out to belong at the viewer, not in the SSOT. ## Why not strip the SSOT Measured it first: stripping every authored span and re-running the generator recovers only **306 of 510 (60%)**. The 204 losses are structural, not a gap to close — | Class | Count | Recoverable | |---|---|---| | Ambiguous commands in prose (`ov`, `less`, `cd`, `rm`) | 48 | No — excluded by design | | Multi-word command lines (`fish-deps install`, `set -U`) | 40 | No | | Literals/placeholders (`!!`, `always`, `on`/`off`, `__fish_config_op_aliases_<slug>`) | 69 | No | | Bare env vars, kebab names, `script(1)`, numbers | 30 | Partly | Teaching `codespans.py` to wrap `ov` or `set -U` in running prose is precisely the over-wrapping it is built to avoid, and stripping would have flattened the man page too. ## What this does instead `bat` dims the backticks and gives span content **the same colour as the surrounding prose** — the delimiters carried no information at all. So each span is rendered bold and the delimiters dropped, in every branch of the viewer chain (`ov`+`bat`, `ov` alone, `bat` alone, `less`, `cat`; `man -l` needs nothing). Both substitutions are line-preserving, so the tail-slice that opens the pager on a section still lands on it. With the viewer no longer showing them, the SSOT is free to keep its markup, and `build_concat()` now runs the same `codespans` pass the site does — so a token is typeset the same way wherever it is read (549 → 662 spans in the concat, and the man page gains code formatting it never had). ## Cleanups this surfaced - **22 function doc-headers** carried 41 hand-written spans, against the rule CONTRIBUTING already states. They reached `config-help` and the man page as literal characters inside a verbatim block. - **12 lines** had backticks inside four-space blocks, which are verbatim in every renderer — literal characters on the site too, inside a code fence. - **One span wrapped a line break**, so the pager showed both halves. - `codespans` now protects indented blocks. Without it the table of contents came out with `ov`, `bat`, `less` and `cat` wrapped inside a code block. ## Verification - `verify-manual.py` 74/74, `tests/run-tests.fish` 220/220 lint + 10/10 - Site output confirmed byte-identical under the `codespans` change alone - End-to-end: **1236 → 0** literal backticks in every viewer path, line counts preserved - `test_codespans_is_site_only` asserted the opposite guarantee and was passing only because its example sat inside a newly-protected block; replaced by tests for what is now true **Trade-off:** the site loses 22 spans (`install`/`sync` and similar ambiguous words the detector won't wrap on sight) as the cost of holding the plain-text header rule. Easy to hand back if you'd rather keep them. The man page is left for CI to regenerate — `pandoc` isn't needed to build the concat.
rootiest added 5 commits 2026-09-01 02:03:31 +00:00
CONTRIBUTING states doc-headers are written as plain text -- the header
is read as-is by config-help, by funcsave, and by anyone opening the
file, and docs/codespans.py adds the site's inline code spans at render
time. 22 files had drifted from that, carrying 41 hand-written spans
that reached config-help and the man page as literal backtick
characters inside an otherwise verbatim block.

The one span whose content ended in a space is requoted rather than
dropped, so "read> " keeps reading as a prompt string.
A token was typeset by whichever pipeline happened to render it: the
site marked tmux and local.fish through codespans, while the man page
and config-help marked only what the SSOT had backticked by hand. Run
the same pass in build_concat() so prose is marked identically wherever
it is rendered (549 -> 662 spans in the concat).

codespans now treats a four-space block as code. The site never meets
one -- prettify() has already turned it into a fence by then -- but the
concat keeps the indented form pandoc wants, and its contents are
verbatim: without this the table of contents alone would come out with
ov, bat, less and cat wrapped inside a code block. Section 5 is
unaffected for the same reason; its entries arrive as indented blocks
that pandoc already sets in a monospace font.

test_codespans_is_site_only asserted the opposite guarantee and was
passing only because its example, -r/--resume, sits inside one of those
newly-protected blocks. It is replaced by tests for what is now true:
indented blocks stay verbatim, prose spans reach the concat, and
section 5 carries no backticks.

The man page is left for CI to regenerate; pandoc is not needed to
build the concat.
Two shapes reach a reader as literal punctuation rather than markup:

A backtick inside a four-space block. The block is verbatim in every
renderer -- pandoc sets it monospace, prettify() fences it for the site
-- so the backtick is a character on the page. Twelve such lines are
cleaned; each sat at the start of its column, so the alignment of the
C5 capture table and the component summaries is unchanged.

A span split over a line break. Markdown pairs it happily, but
config-help pairs backticks one line at a time, so `fish-deps\nupdate`
showed both halves. The sentence is reflowed.

Both are now enforced, the first by test_concat_section_five_stays_
verbatim and the second by test_concat_code_spans_never_straddle_a_line.
config-help pipes the manual through bat, which dims the backticks and
leaves the span content the same colour as the prose around it -- so a
delimiter carried no information and every span cost the reader two
literal characters. 1236 of them across the document.

Each span is now rendered bold and the delimiters dropped, in every
branch of the viewer chain:

- ov + bat, and ov alone, style the spans (bat's output wraps each
  backtick in its own SGR sequence; raw Markdown is matched directly)
- bat alone flattens them on the way in instead, because bat escapes
  any SGR sequence handed to it as input
- less and cat style them, less gaining -R to render the result
- man -l needs nothing; pandoc consumed the backticks at build time

Both substitutions are line-preserving, so the tail-slice that opens
the pager on a requested section still lands on it.
The man page is left to CI, which has pandoc.
rootiest merged commit 81d26e095a into main 2026-09-01 02:11:27 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rootiest/fish-config#121