Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
ddaff9d
feat(site) Stack sticky member and group headings
tony Oct 17, 2026
4c8a73c
fix(site) Keep sticky member bars clear of focus and arrivals
tony Oct 18, 2026
17ce33e
test(site) Check sticky member headings in real browsers
tony Oct 18, 2026
4bba3c4
fix(site) Keep an in-flow Members bar above the gap cover
tony Oct 18, 2026
1947e58
fix(api-model) Keep keyword-argument literals unlinked
tony Oct 18, 2026
db9c274
fix(site) List every parameter in every port
tony Oct 18, 2026
89ab7df
fix(site) Link the tmux CLI Manual and C reference from the sidebar
tony Oct 18, 2026
502dd53
feat(site[tmux-links]) Badge and preview tmux commands, options, hook…
tony Oct 18, 2026
5339763
fix(site[api]) Center declaration controls on the first signature line
tony Oct 18, 2026
a7cb1f3
fix(site) Trail table row permalinks after cell text
tony Oct 18, 2026
b7a726a
feat(api-model[type-params]) Link type parameters to their declaration
tony Oct 18, 2026
a82168a
feat(site[type-params]) Show and link type parameters on reference pages
tony Oct 18, 2026
0c7cec3
chore(site[data]) Regenerate API models with type parameters
tony Oct 18, 2026
5913e51
feat(site) Render tmux manual keys as kbd
tony Oct 18, 2026
0fcab63
feat(site) Render tmux keys in guides as kbd
tony Oct 18, 2026
f44120d
test(site) Check tmux key caps in the browser
tony Oct 18, 2026
3adb7ff
fix(site) Keep link underlines off kind badges
tony Oct 18, 2026
22973b5
fix(site) Centre kind badges on link names at a quarter em
tony Oct 18, 2026
585d8b0
fix(site) Keep card links out of link previews
tony Oct 18, 2026
cd12dcb
fix(site) Let member body padding alone set its inset
tony Oct 18, 2026
b3d776b
feat(site[workspace-links]) Link workspace commands, keys and MCP nam…
tony Oct 18, 2026
cdc01c0
feat(site[mcp]) Resolve Sphinx roles and link tmux names in MCP descr…
tony Oct 18, 2026
7dad77c
feat(site[mcp]) Match MCP argument lists to the API parameter style
tony Oct 18, 2026
3075bd7
test(site[mcp]) Expect the literal chip on linked tool names
tony Oct 18, 2026
9ae3984
feat(site[examples]) Quote tested example lines by anchored excerpt
tony Oct 18, 2026
58a2935
docs(site[examples]) Build the native tmux examples up to their scripts
tony Oct 18, 2026
422c65b
fix(api-model[javadoc]) Keep the indentation inside pre examples
tony Oct 18, 2026
64cc635
chore(site[data]) Regenerate the Java model with indented examples
tony Oct 18, 2026
55f73a1
feat(scripts[tmux-catalog]) Extract tmux options, hooks and format va…
tony Oct 18, 2026
932e097
feat(site[tmux-catalog]) Browse tmux options, hooks and formats
tony Oct 18, 2026
a5e4c99
feat(site[tmux-links]) Link options, hooks and formats to the catalog
tony Oct 18, 2026
dbafe9a
docs(notes[tmux-catalog]) Describe the tmux catalog
tony Oct 18, 2026
f7b59e2
fix(api-model[doxygen]) Keep code listings as fenced blocks
tony Oct 18, 2026
cf09df8
test(scripts) Fail when an example loses its comment indentation
tony Oct 18, 2026
b0b0ae7
fix(site[test]) Compare badge paints by perceptible change
tony Oct 18, 2026
a4e127e
fix(site[rust]) Leave an undocumented self receiver unlinked
tony Oct 18, 2026
1dd9601
fix(site) Keep the concept explanation out of menus
tony Oct 18, 2026
070d1bc
fix(site) Centre each kind badge's mark on its own ink
tony Oct 18, 2026
0f054f0
fix(site[header]) Head each product in the header menus with a label
tony Oct 18, 2026
2f5da28
test(site[header]) Check the header menu headings in the browser
tony Oct 18, 2026
4d32306
feat(site[registry]) Record each Python package's release stage
tony Oct 18, 2026
da30ea6
feat(site[picker]) Badge each product beside its name with a hover card
tony Oct 18, 2026
3a32ec6
test(site[picker]) Check each product's badge and hover card in the b…
tony Oct 18, 2026
c9a48fa
test(site[header]) Refuse a paragraph in a header menu row
tony Oct 18, 2026
1c10e4a
fix(site) Size kind badges to centre their marks on whole pixels
tony Oct 18, 2026
358bb53
test(site[picker]) Wait for the requested port's solution menu
tony Oct 18, 2026
b063882
test(site[home]) Read only destinations when checking the solution menu
tony Oct 18, 2026
7b84e69
feat(site[meta]) Move pages about the site under /meta/
tony Oct 18, 2026
97a8ffc
feat(site[meta]) Move the third-party notices under /meta/
tony Oct 18, 2026
8467c22
feat(site[mcp]) Remove the cross-port MCP overview page
tony Oct 18, 2026
0ca13fd
fix(site) Link tmux releases from the tmux sidebar, not the build slug
tony Oct 18, 2026
5e82e9e
fix(site[meta]) Link the tmux manual from the support page in every l…
tony Oct 18, 2026
2f3fd8d
fix(site[picker]) Space the status badge from its name in the menu bu…
tony Oct 18, 2026
5a32486
feat(site[home]) Title the home page "libtmux: pilot tmux in your lan…
tony Oct 18, 2026
8a7dfb4
fix(site[status]) Open a status card after the native tooltip delay
tony Oct 18, 2026
934e7bd
fix(site[picker]) Drop the rule and gap around a product's other refe…
tony Oct 18, 2026
bf528e1
feat(site[demo]) Add kind badge candidates page
tony Oct 18, 2026
e0f5583
feat(site[demo]) Add 25 badge candidates in four sets
tony Oct 18, 2026
16fe9ef
feat(site[demo]) Add badges-in-context demo page
tony Oct 18, 2026
d762ba2
fix(site[demo]) Build the legend chip as one token
tony Oct 18, 2026
666fe18
feat(site[demo]) Drop left-accent tags and two-letter marks
tony Oct 18, 2026
0a31b2e
fix(site[demo]) Centre badge marks by ink on whole pixels
tony Oct 18, 2026
53426d7
fix(site[demo]) Centre the legend word by its x-height
tony Oct 18, 2026
97e4315
fix(site[demo]) Put chip text on one baseline
tony Oct 18, 2026
5ddcfe4
feat(site[badges]) Build the kind mark on six hue families
tony Oct 18, 2026
918a7c5
feat(site[badges]) Render the mark in lists, cards, search and hover …
tony Oct 18, 2026
cd0a72a
feat(site[badges]) Mark the first mention in a section, link the rest…
tony Oct 18, 2026
3e72d78
test(site[badges]) Gate the mark's centring and the mention density
tony Oct 18, 2026
638bcab
fix(site[badges]) Space and centre the mark that leads a name
tony Oct 18, 2026
c808b1c
fix(site[badges]) Hold each related-APIs chip in its own row
tony Oct 18, 2026
cfdde72
feat(site[badges]) Drop the gutter and the redundant mark in the MCP …
tony Oct 18, 2026
2949ab5
feat(site[badges]) Draw modifiers as outlines on the legend chip's box
tony Oct 18, 2026
c399b7e
refactor(site[reference]) Move the qualified name into the source dis…
tony Oct 18, 2026
171d2f7
feat(site[badges]) Put the version chip and the other-language kind o…
tony Oct 18, 2026
2b756c7
fix(api-model[overload]) Reserve the overload modifier for callables
tony Oct 18, 2026
0ec2ced
test(site[badges]) Run the modifier control on a real modifier
tony Oct 18, 2026
c9fcd04
feat(site[badges]) Draw no mark under a protocol section of one kind …
tony Oct 18, 2026
a715528
feat(site[badges]) Write the kind in the symbol index as the plain ki…
tony Oct 18, 2026
231804a
test(site[badges]) Keep the older badge gates in step with the new rows
tony Oct 18, 2026
924496b
fix(site[ports]) State tmux 3.3a as the floor for eight ports
tony Oct 18, 2026
4ddbc46
fix(site[anchors]) Name table rows by their visible label, not a hidd…
tony Oct 18, 2026
68ae0e6
test(site[mcp]) Render the Python MCP picker instead of the removed /…
tony Oct 18, 2026
c65a536
fix(site[swift]) State tmux 3.3a as the Swift floor
tony Oct 18, 2026
33b8519
test(scripts) Count the Ruby and Swift parameter lists in the type-li…
tony Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
54 changes: 50 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ $ pnpm test
The outer loop includes the `*.outer.test.ts` integration suites, which seed
the full API catalog or exercise subprocess shutdown. It also adds type
checks and starts its own Astro development server
from current source. Browser checks cover prose, an MCP table, API equivalents,
and phone dropdown placement at 1440, 768, and 390 pixels. The sampled renderer
also checks homepage choices and uses `node site/scripts/check-dev.mjs --sample`.
It does not reuse `_site`. The runtime budgets are under 2, 10, and 60 seconds for inner,
from current source. Browser checks cover prose, an MCP table, row permalinks,
API equivalents, and phone dropdown placement at 1440, 768, and 390 pixels. The
sampled renderer also checks homepage choices and uses
`node site/scripts/check-dev.mjs --sample`. It does not reuse `_site`. The
runtime budgets are under 2, 10, and 60 seconds for inner,
medium, and outer respectively; measure the complete pnpm command when
changing a loop. `test:fast` aliases medium. The runner stops an over-budget
loop and fails.
Expand Down Expand Up @@ -131,6 +132,36 @@ Install the pinned Chromium build for local browser checks with
that build and its system dependencies before the publication audit.
The standard ten-second browser timeout increases to twenty seconds on CI.

Each engine aligns the sticky Members and group headings of a reference page
differently. The publication audit checks them in Chromium; install Firefox
and WebKit to check all three:

```console
$ pnpm --filter @libtmux/site exec playwright install firefox webkit
```

Then run the sticky heading check, which starts its own server and drives
focus, link, repeated link and typed hash arrivals in each engine:

```console
$ node site/scripts/check-dev.mjs --api-sticky
```

Kind marks centre their ink by measurement, and a legend chip, split chip or
modifier chip is one line of text. The badge gate reads the pixels of real pages
at 1x and at a real 2x device scale factor (Chromium launched with the factor,
because the emulated factor snaps text to CSS pixels), in light and dark. It
measures the mark that leads a name in cards and lists, the related-APIs
chips, the group headings, the MCP sidebar's rule (a mark only in a group that
mixes kinds, and no gutter) and that the API tree keeps its gutter, and it
checks that only the first mention of a name in a section carries a mark. Each
check ends with a negative control that breaks what it holds and requires the
gate to notice:

```console
$ node site/scripts/check-dev.mjs --badge-centring
```

Keep the complete assembly, all output suites, link audits, source/model
freshness, and full browser matrix in the publication audit:

Expand Down Expand Up @@ -177,6 +208,21 @@ the index:
$ node scripts/gen-mentions.mjs
```

Workspace prose links a code span that names a CLI command, option or
configuration key to its entry in the port's CLI Manual or Configuration
reference. Those destinations come from an index of the workspace pages. After
adding or renaming a CLI page, a table of options, or a table of keys,
regenerate it:

```console
$ node scripts/gen-workspace-entities.mjs
```

A bare name links only on a user-facing workspace page, where it outranks a
library symbol or tmux name of the same spelling. `<program> <command>` links
on any page of a port that ships the program. Internals pages and the language
API index keep their library symbols.

The publication audit checks index freshness, rendered links, navigation targets,
and table layout at desktop, tablet, and phone widths.

Expand Down
10 changes: 10 additions & 0 deletions WRITING.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,16 @@ Run the workspace linter:
$ pnpm run lint
```

### tmux keys

Write a tmux key in tmux's notation, in backticks: `C-b d` for a sequence,
`M-x`, `C-c`. The site renders it as a key cap; Markdown and agent exports
keep the backticks. A modified key, and any key in a list with one, always
qualifies. A bare name or character such as `Enter` or `q` qualifies beside the
word "key" ("the `Enter` key") or "press". Text typed into a pane, like the
word `Enter`, stays code. [`tmux-keys.ts`](site/src/lib/tmux-keys.ts) holds the
rule, and the manual pages apply it to the manual's own key tables and bindings.

### Durable links

Link to repository-relative files for local guidance. For external source
Expand Down
24 changes: 24 additions & 0 deletions notes/gp-sphinx-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,30 @@ per-language table is in `packages/api-model/src/doc/roles.ts`; adding a
syntax means adding a matcher, not widening an existing one, because the
guards are what keep `[see below]` from becoming a link.

**Badges: kind is a filled chip, a modifier is an outline on the same box.**
gp-sphinx draws a modifier as a monospace outline pill at 0.68rem, with its hue
scoped to `dl.py`. This site draws a kind as the legend chip (the mark and the
word in one filled token) and a modifier on that chip's geometry: the same
snapped height, radius, sans 600 word and baseline, so a declaration row reads
as one family and an entry has one loud thing. Where it differs:

- The modifier keeps gp-sphinx's hue for the same word (async violet, abstract
indigo, classmethod and static amber, final and readonly green, the rest a
readable grey) from the same `--gp-sphinx-badge-mod-*` tokens, but applies
it wherever a modifier sits, not only inside `dl.py`. The member list's
`inherited` and a card's context badge are modifiers too.
- The light amber ink is five percent darker than the token. The token is
under 4.5:1 on the declaration band, and the darker ink keeps the hue. Dark
mode takes the token as it is.
- `package` is an outline in the types hue, no longer a second filled badge.
- `deprecated` is a status and takes the warning ink and border, which the
vendored `dl.py` default would otherwise repaint grey.
- The word is not lowercased by CSS: a package name keeps its case.

`check-style-parity.mjs` compares the eight treatments above and none of them
is a badge, so this divergence is held by `test/modifier-chips.test.ts` and the
modifier chips in `check-badge-centring.mjs`, not by that check.

**The page shell is this site's, not Furo's.** Header, footer, search, version
switcher and port switcher are shared with the rest of libtmux.org. Only the
reference *content* is held to parity. Under reduced motion the API surface
Expand Down
2 changes: 1 addition & 1 deletion notes/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ field at all) — resolves every ledger's cited evidence against that port's
own checkout (a test file for .NET, a test function for Go, a contract
test class for Java), and classifies each row `test-verified` / `claimed`
/ `unknown` by what actually resolves, not by how confident the ledger's
prose sounds. `site/src/pages/parity.astro` renders the result:
prose sounds. `site/src/pages/meta/parity.astro` renders the result:
per-port summaries, a cross-port module table, and an explicit note that
the four ledgers are not pinned to the same Python revision (.NET and Java
pin `c4a980b`, Swift pins `988b02a`, Go records none). Spot-checked: Java's
Expand Down
104 changes: 104 additions & 0 deletions notes/tmux-catalog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# tmux catalog: options, hooks and format variables

Prose across the site names tmux options (`destroy-unattached`), hooks
(`session-created`) and format variables (`session_attached`) that the
CLI Manual and the C source reference do not index. The catalog is a browser
for those names, built from the same data tmux uses, so any page can link to
the entry for the tmux release it describes.

## Entry kinds

| Kind | Fields | Source in the tmux tree |
| ---- | ------ | ----------------------- |
| `option` | scope (`server`, `session`, `window`, `pane`), type, default, minimum, maximum, unit, choices, array and style flags, accepted spellings | A row of `options_table[]` in `options-table.c`; choice lists, symbolic defaults and ranges resolve through the same file and `tmux.h`. |
| `hook` | scope, the command an `after-` hook follows, control mode notification of the same name, firing sites | `OPTIONS_TABLE_*HOOK` rows of `options_table[]`; firing sites are the literal names passed to `notify_*` and `cmdq_insert_hook` in `*.c`. |
| `format` | type (`string` or `time`), the object it describes, short form, callback | `format_table[]` in `format.c`, plus names the manual documents that are set elsewhere (`format_add`, `cmdq_add_format`, `%s_suffix` patterns). The object is the first `ft->` member the callback reads. |

Descriptions come from the tmux.1 of the same release: the `OPTIONS` list, the
`HOOKS` list (a hook without its own text falls back to its `%name` entry in
`CONTROL MODE`, and an `after-` hook to the shared after-hook paragraph) and
the `Variable name` tables. An option the manual omits keeps the `.text` from
the option table and says so. A format variable the manual omits is listed as
undocumented rather than dropped.

Other tables would justify more kinds (control mode notifications, layouts,
key tables). They are not part of this change.

## Extraction

`scripts/gen-tmux-catalog.mjs` reads the pinned commit of each release in
`site/src/data/tmux/versions.json` and writes
`site/src/data/tmux/catalog/<version>.json`. Output is sorted and
deterministic. Every entry carries `file:line` at that commit.

The script refuses to guess: an unresolved symbol in a table cell, a format
variable with no source location, or a manual snapshot whose `manualSha256`
differs from `tmux.1` at the commit stops the run. `--check` verifies digests
and shape without a checkout, and with `--source` regenerates and compares.
`gen-tmux-catalog.negative.mjs` shows each failure.

## Pages and URLs

```text
/tmux/<version>/catalog/ index
/tmux/<version>/catalog/options/ kind page, grouped like the tree
/tmux/<version>/catalog/options/<name>/ entry page
/tmux/<version>/catalog/hooks/<name>/
/tmux/<version>/catalog/formats/<name>/
/tmux/<version>/catalog/tree.json sidebar tree and search inventory
/tmux/<version>/catalog/<kind>/<name>.preview.json
```

`<version>` is a pinned release or `latest`. The page uses the C reference's
layout: a sidebar tree with search and All, Options, Hooks and Formats tabs, kind
badges, and an entry page with facts, description, releases, source and
"Used by". A name missing from another release links that release's section.

The picker and the tmux sidebar list it as "Options, hooks and formats", a
third section beside "CLI Manual" and "C source reference".

## Linking

`tmuxEntityHref(kind, name, version)` in `site/src/lib/tmux-links.ts` is the one
place a tmux name becomes a destination. A command resolves to its CLI Manual
page; an option, hook or format variable resolves to its catalog entry, and an
entry page links back to the manual text through `tmuxManualEntityHref`.
`tmuxMentions` finds the names in a run of text, and every surface uses it: API
docstrings (`ApiDoc`), guides and topics (`rehype-api-links`), their Markdown
exports (`prose-markdown`, `symbol-markdown`) and the CLI Manual HTML
(`tmux-catalog-html.ts`). Hover cards for catalog links come from
`<entry>.preview.json`.

Which options, hooks and variables count as a mention is decided in
`tmux-catalog-links.ts`, in this order:

1. Only code literals link. Prose names an entry without being a reference to
it.
2. A language API link wins. A name the library documents as its own symbol
links there; the catalog link is the fallback, and a command claims a span
that holds one.
3. A whole literal links, quoted or not, with an array index
(`after-new-window[0]`). A hyphenated option or hook links as a separate word
of a longer run, and an underscored variable links inside `#{...}`.
4. A single word (`status`, `host`) links only beside the word `option`, `hook`
or `variable`, as the argument of `set-option`, `set-hook` and their read
forms, or as `#{word}`. A name more than one kind uses, or a command's name,
links only when a neighbouring word picks the kind; otherwise it stays plain.
5. Inside `#{...}` only the variable position links (after `#{`, `#{?` or
`E:`, `T:`, `t:` and similar); words after a comparison stay literal.
6. Displayed examples, flags (`--status-left`), control notifications
(`%session-renamed`), user options (`@name`) and paths never link.
7. A name absent from the selected release stays plain.

Library members that wrap a family (`set_hook`, `show_hooks`, `Hooks`) link to
the matching catalog section through the concepts in
`packages/api-model/src/concepts.ts` and the wrapper types listed in
`tmux-catalog.ts`. An entry page lists, per port, the members whose
documentation names it, and a C declaration (`options_table`, `format_cb_*`,
`notify_*`) links to its catalog entries.

## Open questions

- The URL plural (`options`, `hooks`, `formats`) against the singular used for
kinds in code.
- Whether single-word names should ever link without a context word.
10 changes: 10 additions & 0 deletions packages/api-model/src/doc/csharp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ function prose(xml: string): string {
export interface ParsedXmlDoc {
doc: DocBlock
params: Map<string, string>
/** `<typeparam name="T">`, by parameter name. */
typeParams: Map<string, string>
returnsDoc?: string
raises: { type: string; doc?: string }[]
}
Expand All @@ -95,6 +97,13 @@ export function parseXmlDoc(raw: string): ParsedXmlDoc {
if (name && text) params.set(name, text)
}

const typeParams = new Map<string, string>()
for (const s of sections(raw, 'typeparam')) {
const name = attr(s.attrs, 'name')
const text = prose(s.body)
if (name && text) typeParams.set(name, text)
}

const returnsDoc = prose(sections(raw, 'returns')[0]?.body ?? '') || undefined

const raises = sections(raw, 'exception')
Expand Down Expand Up @@ -136,6 +145,7 @@ export function parseXmlDoc(raw: string): ParsedXmlDoc {
examples: examples.length ? examples : undefined,
},
params,
typeParams,
returnsDoc,
raises,
}
Expand Down
2 changes: 1 addition & 1 deletion packages/api-model/src/doc/go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,5 @@ export function parseGoDoc(raw: string, parser: Parser): ParsedMarkdownDoc {
}
const first = blocks[0]?.kind === 'prose' ? blocks.shift()!.text : ''
return { doc: { summary: first.replace(/\s+/g, ' ').trim(), body: blocks.map((block) => block.text).join('\n\n') || undefined },
params: new Map(), raises: [] }
params: new Map(), typeParams: new Map(), raises: [] }
}
11 changes: 8 additions & 3 deletions packages/api-model/src/doc/javadoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { parseMarkdownDocFull, type ParsedMarkdownDoc } from './markdown.ts'
* the block's spelling rather than an inline literal, and an inline pass that
* reached it first would leave the fence holding a stray brace.
*/
const PRE_BLOCK = /<pre>\s*(?:\{@code\b)?\s*\n?([\s\S]*?)\n?\s*\}?\s*<\/pre>/g
const PRE_BLOCK = /<pre>\s*(?:\{@code\b)?[ \t]*\n?([\s\S]*?)\n?\s*\}?\s*<\/pre>/g

/** Inline formatting, as `[tag, markdown]`. `<code>` is javadoc's `{@code}`. */
const INLINE: [RegExp, string][] = [
Expand All @@ -35,11 +35,16 @@ const INLINE: [RegExp, string][] = [
[/<code>([\s\S]*?)<\/code>/g, '`$1`'],
]

const indentation = (line: string): number => /^[ \t]*/.exec(line)![0].length

/** Translate one javadoc comment body into Markdown. */
export function javadocToMarkdown(raw: string): string {
let text = raw.replace(PRE_BLOCK, (_, code: string) => {
const lines = code.split('\n').map((l) => l.replace(/^\s*/, ''))
return `\n\`\`\`java\n${lines.join('\n')}\n\`\`\`\n`
const lines = code.split('\n')
const margin = Math.min(...lines.filter((line) => line.trim()).map(indentation))
// Only the block's common margin comes off; the rest is the code's own indentation.
const body = lines.map((line) => (line.trim() ? line.slice(margin) : '')).join('\n')
return `\n\`\`\`java\n${body}\n\`\`\`\n`
})

// `<p>` opens a paragraph in javadoc and closes nothing; every one of
Expand Down
25 changes: 17 additions & 8 deletions packages/api-model/src/doc/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const HEADING = /^\s*#{1,3}\s+(Examples?|Panics|Errors|Safety|Notes?|Returns?|Ar
* and none of them reached a field list, because this parser treated the
* whole comment as prose.
*/
const BLOCK_TAG = /^\s*@(param|arg|argument|returns?|throws|exception|deprecated|since)\b\s*(.*)$/
const BLOCK_TAG = /^\s*@(param|arg|argument|returns?|throws|exception|deprecated|since|typeParam|typeparam|template|tparam)\b\s*(.*)$/

/**
* A Markdown link definition: `` [`Window`]: crate::Window ``.
Expand Down Expand Up @@ -124,6 +124,8 @@ function callout(quoted: string[]): { kind: string; text: string } | undefined {
export interface ParsedMarkdownDoc {
doc: DocBlock
params: Map<string, string>
/** `@typeParam T`, `@template T`, `@tparam T`, and Javadoc's `@param <T>`, by parameter name. */
typeParams: Map<string, string>
returnsDoc?: string
raises: { type: string; doc?: string }[]
}
Expand Down Expand Up @@ -157,6 +159,7 @@ export function parseMarkdownDocFull(raw: string, defaultLang = 'text'): ParsedM
let inExamples = false

const params = new Map<string, string>()
const typeParams = new Map<string, string>()
const raises: { type: string; doc?: string }[] = []
const admonitions: { kind: string; text: string }[] = []
/** The blockquote currently collecting lines, callout or not. */
Expand All @@ -177,7 +180,10 @@ export function parseMarkdownDocFull(raw: string, defaultLang = 'text'): ParsedM
const closeTag = () => {
if (!tag) return
const text = tag.text.join(' ').replace(/\s+/g, ' ').trim()
if (tag.kind === 'param' && tag.name) params.set(tag.name, text)
const javadocTypeParam = tag.kind === 'param' ? /^<(\w+)>$/.exec(tag.name ?? '')?.[1] : undefined
if (tag.kind === 'typeparam' && tag.name) typeParams.set(tag.name, text.replace(/^-\s*/, ''))
else if (javadocTypeParam) typeParams.set(javadocTypeParam, text)
else if (tag.kind === 'param' && tag.name) params.set(tag.name, text)
else if (tag.kind === 'returns') returnsDoc = text || undefined
else if (tag.kind === 'throws' && tag.name) raises.push({ type: tag.name, doc: text || undefined })
tag = undefined
Expand Down Expand Up @@ -224,15 +230,17 @@ export function parseMarkdownDocFull(raw: string, defaultLang = 'text'): ParsedM
closeTag()
const kind = /^(param|arg|argument)$/.test(block[1])
? 'param'
: /^returns?$/.test(block[1])
? 'returns'
: /^(throws|exception)$/.test(block[1])
? 'throws'
: block[1]
: /^(typeParam|typeparam|template|tparam)$/.test(block[1])
? 'typeparam'
: /^returns?$/.test(block[1])
? 'returns'
: /^(throws|exception)$/.test(block[1])
? 'throws'
: block[1]
// `@param name description` — the name is the first word for param and
// throws, and absent for the rest.
const rest = block[2] ?? ''
const needsName = kind === 'param' || kind === 'throws'
const needsName = kind === 'param' || kind === 'throws' || kind === 'typeparam'
const [first, ...tail] = rest.split(/\s+/)
tag = needsName
? { kind, name: first || undefined, text: tail }
Expand Down Expand Up @@ -278,6 +286,7 @@ export function parseMarkdownDocFull(raw: string, defaultLang = 'text'): ParsedM
admonitions: admonitions.length ? admonitions : undefined,
},
params,
typeParams,
returnsDoc,
raises,
}
Expand Down
Loading
Loading