Skip to content

Commit cc1d07e

Browse files
committed
improve landing pages and clean rendered signatures
1 parent 01f01c6 commit cc1d07e

7 files changed

Lines changed: 103 additions & 12 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Doxygen XML to Markdown converter for C++ and Java developers who want minimal,
1515
- **Markdown comments** - Markdown in Doxygen comments is rendered
1616
- **Doxygen groups** - [grouping](http://www.doxygen.nl/manual/grouping.html) support for organised docs
1717
- **Grouped and ungrouped codebases** - works with explicit grouped compounds and normal namespace/class output
18+
- **Structured landing pages** - namespace and group pages separate nested namespaces, types, and enums
1819
- **Custom templates** - modify the Handlebars templates to suit your needs
1920
- **Optional index** - optionally render a top-level index
2021

src/helpers.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export function inline(code: string | string[]): string {
4747
return s + (isInline ? '`' : '');
4848
}
4949

50+
/**
51+
* Strip markdown links from generated type/signature text while preserving labels.
52+
*/
53+
export function stripMarkdownLinks(text: string): string {
54+
return (text || '').replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
55+
}
56+
5057
/**
5158
* Generate an anchor string based on options.
5259
*/

src/templates.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { readFileSync, readdirSync } from 'node:fs';
99
import { join } from 'node:path';
1010
import Handlebars from 'handlebars';
11-
import { getAnchor, cleanId } from './helpers.js';
11+
import { getAnchor, cleanId, stripMarkdownLinks } from './helpers.js';
1212
import type { AnchorMap } from './helpers.js';
1313
import { log } from './logger.js';
1414
import type { Compound, MoxygenOptions } from './types.js';
@@ -37,6 +37,25 @@ export function registerHelpers(options: Pick<MoxygenOptions, 'anchors' | 'htmlA
3737

3838
Handlebars.registerHelper('or', (a: unknown, b: unknown) => a || b);
3939

40+
Handlebars.registerHelper('compoundsOfKind', (compounds: unknown, ...args: unknown[]) => {
41+
const options = args[args.length - 1];
42+
void options;
43+
const kinds = new Set(
44+
args
45+
.slice(0, -1)
46+
.filter((value): value is string => typeof value === 'string'),
47+
);
48+
if (!Array.isArray(compounds) || !kinds.size) {
49+
return [];
50+
}
51+
return compounds.filter((compound) =>
52+
compound &&
53+
typeof compound === 'object' &&
54+
'kind' in compound &&
55+
kinds.has((compound as Record<string, unknown>).kind as string),
56+
);
57+
});
58+
4059
Handlebars.registerHelper('shortname', (fullname: string) => {
4160
const parts = (fullname || '').split('::');
4261
return parts[parts.length - 1] || fullname;
@@ -52,27 +71,35 @@ export function registerHelpers(options: Pick<MoxygenOptions, 'anchors' | 'htmlA
5271
if (kind === 'variable') {
5372
const init = member.initializer as string;
5473
return init
55-
? `${member.returnType} ${member.name} ${init}`
56-
: `${member.returnType} ${member.name}`;
74+
? `${stripMarkdownLinks(member.returnType as string)} ${member.name} ${init}`
75+
: `${stripMarkdownLinks(member.returnType as string)} ${member.name}`;
5776
}
5877
if (kind === 'property') {
59-
return `${member.returnType} ${member.name}`;
78+
return `${stripMarkdownLinks(member.returnType as string)} ${member.name}`;
6079
}
6180

6281
// function/signal/slot
6382
const parts: string[] = [];
6483
const tparams = member.templateParams as Array<{ type: string; name: string }>;
6584
if (tparams && tparams.length > 0) {
66-
parts.push('template<' + tparams.map(tp => tp.name ? `${tp.type} ${tp.name}` : tp.type).join(', ') + '>');
85+
parts.push('template<' + tparams.map(tp => {
86+
const type = stripMarkdownLinks(tp.type);
87+
return tp.name ? `${type} ${tp.name}` : type;
88+
}).join(', ') + '>');
6789
}
6890
if (member.isVirtual) parts.push('virtual');
6991
if (member.isStatic) parts.push('static');
7092
if (member.isInline) parts.push('inline');
7193
if (member.isExplicit) parts.push('explicit');
7294
const rt = member.returnType as string;
73-
if (rt) parts.push(rt);
95+
if (rt) parts.push(stripMarkdownLinks(rt));
7496
const params = member.params as Array<{ type: string; name: string }>;
75-
const paramStr = params ? params.map(p => p.name ? `${p.type} ${p.name}` : p.type).join(', ') : '';
97+
const paramStr = params
98+
? params.map((p) => {
99+
const type = stripMarkdownLinks(p.type);
100+
return p.name ? `${type} ${p.name}` : type;
101+
}).join(', ')
102+
: '';
76103
parts.push(`${member.name}(${paramStr})`);
77104
const qualifiers = member.qualifiers as string[];
78105
if (qualifiers) {

templates/cpp/namespace.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,38 @@
1010
{{detaileddescription}}
1111
{{/if}}
1212

13-
{{#if filtered.compounds}}
13+
{{#with (compoundsOfKind filtered.compounds "namespace") as |namespaces|}}
14+
{{#if namespaces}}
15+
### Namespaces
16+
17+
| Name | Description |
18+
|------|-------------|
19+
{{#each namespaces}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
20+
{{/each}}
21+
{{/if}}
22+
{{/with}}
23+
24+
{{#with (compoundsOfKind filtered.compounds "class" "struct" "interface") as |types|}}
25+
{{#if types}}
1426
### Classes
1527

1628
| Name | Description |
1729
|------|-------------|
18-
{{#each filtered.compounds}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
30+
{{#each types}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
31+
{{/each}}
32+
{{/if}}
33+
{{/with}}
34+
35+
{{#with (compoundsOfKind filtered.compounds "enum") as |enums|}}
36+
{{#if enums}}
37+
### Enumerations
38+
39+
| Name | Description |
40+
|------|-------------|
41+
{{#each enums}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
1942
{{/each}}
2043
{{/if}}
44+
{{/with}}
2145

2246
{{#each filtered.sections}}
2347
### {{label}}

templates/java/namespace.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,38 @@
1010
{{detaileddescription}}
1111
{{/if}}
1212

13-
{{#if filtered.compounds}}
13+
{{#with (compoundsOfKind filtered.compounds "namespace") as |namespaces|}}
14+
{{#if namespaces}}
15+
### Namespaces
16+
17+
| Name | Description |
18+
|------|-------------|
19+
{{#each namespaces}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
20+
{{/each}}
21+
{{/if}}
22+
{{/with}}
23+
24+
{{#with (compoundsOfKind filtered.compounds "class" "struct" "interface") as |types|}}
25+
{{#if types}}
1426
### Classes
1527

1628
| Name | Description |
1729
|------|-------------|
18-
{{#each filtered.compounds}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
30+
{{#each types}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
31+
{{/each}}
32+
{{/if}}
33+
{{/with}}
34+
35+
{{#with (compoundsOfKind filtered.compounds "enum") as |enums|}}
36+
{{#if enums}}
37+
### Enumerations
38+
39+
| Name | Description |
40+
|------|-------------|
41+
{{#each enums}}| [`{{shortname name}}`](#{{cleanId refid name}}) | {{cell summary}} |
1942
{{/each}}
2043
{{/if}}
44+
{{/with}}
2145

2246
{{#each filtered.sections}}
2347
### {{label}}

test/helpers.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from 'vitest';
2-
import { inline, getAnchor, findParent } from '../src/helpers.js';
2+
import { inline, getAnchor, findParent, stripMarkdownLinks } from '../src/helpers.js';
33
import { createCompound } from '../src/compound.js';
44

55
describe('helpers', () => {
@@ -65,4 +65,11 @@ describe('helpers', () => {
6565
expect(result).toBeUndefined();
6666
});
6767
});
68+
69+
describe('stripMarkdownLinks', () => {
70+
it('replaces markdown links with their labels', () => {
71+
const input = '[TrackHandle](#trackhandle) func([PeerSession::State](#state) state)';
72+
expect(stripMarkdownLinks(input)).toBe('TrackHandle func(PeerSession::State state)');
73+
});
74+
});
6875
});

test/integration.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ describe('integration', () => {
9393
expect(content.length).toBeGreaterThan(0);
9494
expect(content).toContain('# widget');
9595
expect(content).toContain('Module page for a widget API documented with file-level grouping.');
96+
expect(content).toContain('### Namespaces');
9697
expect(content).toContain('documented via file-level grouping only.');
9798
expect(content).toContain('createWidget');
9899
expect(content).toContain('Returns the current widget size.');

0 commit comments

Comments
 (0)