Skip to content

Commit 28676b4

Browse files
committed
fix(markdown): keep admonitions and definition lists inside their list
1 parent 232e351 commit 28676b4

9 files changed

Lines changed: 100 additions & 43 deletions

File tree

‎src/cli.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ program
2424
.option('-H, --html-anchors', 'add HTML anchors to internal links', false)
2525
.option('-l, --language <lang>', 'programming language', 'cpp')
2626
.option('-F, --flavour <name>', 'markdown dialect for admonitions: pandoc or github', 'pandoc')
27+
.option('--flavor <name>', 'alias for --flavour')
2728
.option('-t, --templates <dir>', 'custom templates directory')
2829
.option('-s, --source-root <dir>', 'source root used to resolve file-level group membership')
2930
.option('-f, --frontmatter', 'prepend YAML frontmatter to output files', false)
@@ -42,7 +43,7 @@ program
4243
anchors: opts.anchors as boolean,
4344
htmlAnchors: opts.htmlAnchors as boolean,
4445
language: opts.language as string,
45-
flavour: opts.flavour as string,
46+
flavour: (opts.flavor ?? opts.flavour) as string,
4647
templates: opts.templates as string | undefined,
4748
sourceRoot: opts.sourceRoot as string | undefined,
4849
frontmatter: opts.frontmatter as boolean,

‎src/parser.ts‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,19 @@ const ADMONITIONS: Record<string, { container: string; alert: string }> = {
5252
* default; GitHub renders those as literal text and wants blockquote alerts,
5353
* where every line of the body carries the quote marker.
5454
*/
55-
function renderAdmonition(kind: string, body: string, flavour: string): string {
55+
function renderAdmonition(kind: string, body: string, flavour: string, indent: string): string {
5656
const admonition = ADMONITIONS[kind];
5757
const prefixed = kind === 'deprecated' ? `**Deprecated.** ${body}` : body;
5858

59-
if (flavour !== 'github') {
60-
return `\n:::${admonition.container}\n${prefixed}\n:::\n\n`;
61-
}
59+
const lines = flavour === 'github'
60+
? [`> [!${admonition.alert}]`, ...prefixed.trim().split('\n').map((line) => (line.trim() ? `> ${line}` : '>'))]
61+
: [`:::${admonition.container}`, ...prefixed.split('\n'), ':::'];
6262

63-
const quoted = prefixed.trim().split('\n')
64-
.map((line) => (line.trim() ? `> ${line}` : '>'))
65-
.join('\n');
66-
return `\n> [!${admonition.alert}]\n${quoted}\n\n`;
63+
// Inside a list item the whole block has to sit at the item's content
64+
// column, or it ends the list instead of belonging to it, and it closes on a
65+
// single newline so the next item follows on.
66+
const block = lines.map((line) => (line ? indent + line : line)).join('\n');
67+
return `\n${block}\n${indent ? '' : '\n'}`;
6768
}
6869

6970
function listMarker(name: string | undefined): string {
@@ -74,6 +75,13 @@ function enclosingLists(context: XmlElement[]): XmlElement[] {
7475
return context.filter((el) => LIST_ELEMENTS.has(el['#name'] ?? ''));
7576
}
7677

78+
/** Column at which the content of the current list item continues. */
79+
function listContentIndent(context: XmlElement[]): string {
80+
return enclosingLists(context)
81+
.map((el) => ' '.repeat(listMarker(el['#name']).length))
82+
.join('');
83+
}
84+
7785
/**
7886
* Indent for a list item, which Markdown measures against the marker of the
7987
* item it nests under, so an ordered parent indents further than a bullet.
@@ -178,7 +186,9 @@ function toMarkdown(element: unknown, context: XmlElement[] = [], options: Markd
178186
context.push(el);
179187
break;
180188
case 'varlistentry':
181-
s = '\n* ';
189+
// The entry before this one already ended its line, so starting with a
190+
// newline here would put a blank line between every term.
191+
s = listIndent(context) + '* ';
182192
break;
183193
case 'term':
184194
s = '**';
@@ -300,7 +310,7 @@ function toMarkdown(element: unknown, context: XmlElement[] = [], options: Markd
300310
case 'simplesect': {
301311
const closeKind = el.$?.kind;
302312
s = closeKind && ADMONITIONS[closeKind]
303-
? renderAdmonition(closeKind, s, parserOptions?.flavour ?? 'pandoc')
313+
? renderAdmonition(closeKind, s, parserOptions?.flavour ?? 'pandoc', listContentIndent(context))
304314
: `${s}\n\n`;
305315
context.pop();
306316
break;

‎src/templates.ts‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const DEFAULT_RENDER_CONTEXT: RenderContext = {
1919
headingBase: 1,
2020
};
2121

22-
const MARKDOWN_LINK = /\[([^\]]+)\]\(([^)]+)\)/g;
22+
const MARKDOWN_LINK = /\[([^\]]+)\]\(([^)]+)\)/;
23+
const MARKDOWN_LINKS = /\[([^\]]+)\]\(([^)]+)\)/g;
2324

2425
/**
2526
* Render a type for a table cell.
@@ -34,11 +35,8 @@ function typeCell(type: string): string {
3435
const trimmed = (type || '').trim();
3536
if (!trimmed) return '';
3637

37-
MARKDOWN_LINK.lastIndex = 0;
3838
if (!MARKDOWN_LINK.test(trimmed)) return `\`${trimmed}\``;
39-
40-
MARKDOWN_LINK.lastIndex = 0;
41-
return trimmed.replace(MARKDOWN_LINK, '[`$1`]($2)');
39+
return trimmed.replace(MARKDOWN_LINKS, '[`$1`]($2)');
4240
}
4341

4442
function headingLevel(relativeLevel: unknown, context: RenderContext): number {

‎test/fixtures/macros/src/macros.h‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ void flush(void);
5656
1. acquire
5757
2. verify
5858
2. close
59+
60+
Terms:
61+
62+
<dl>
63+
<dt>pending</dt><dd>queued but not started</dd>
64+
<dt>settled</dt><dd>finished either way</dd>
65+
</dl>
66+
67+
Caveats:
68+
69+
- the queue may grow while draining
70+
@note callers should not rely on the count
71+
- draining twice is safe
5972
*/
6073
void drain(void);
6174

‎test/fixtures/macros/xml-out/xml/macros_8h.xml‎

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
</detaileddescription>
9292
<inbodydescription>
9393
</inbodydescription>
94-
<location file="src/macros.h" line="83" column="9" bodyfile="src/macros.h" bodystart="83" bodyend="-1"/>
94+
<location file="src/macros.h" line="96" column="9" bodyfile="src/macros.h" bodystart="96" bodyend="-1"/>
9595
</memberdef>
9696
</sectiondef>
9797
<sectiondef kind="var">
@@ -106,7 +106,7 @@
106106
</detaileddescription>
107107
<inbodydescription>
108108
</inbodydescription>
109-
<location file="src/macros.h" line="77" column="11"/>
109+
<location file="src/macros.h" line="90" column="11"/>
110110
</memberdef>
111111
</sectiondef>
112112
<sectiondef kind="func">
@@ -187,13 +187,31 @@
187187
</listitem><listitem><para>verify</para>
188188
</listitem></orderedlist>
189189
</para>
190-
</listitem><listitem><para>close </para>
190+
</listitem><listitem><para>close</para>
191191
</listitem></orderedlist>
192+
</para>
193+
<para>Terms:</para>
194+
<para><variablelist>
195+
<varlistentry><term>pending</term></varlistentry>
196+
<listitem><para>queued but not started </para>
197+
</listitem>
198+
<varlistentry><term>settled</term></varlistentry>
199+
<listitem><para>finished either way </para>
200+
</listitem>
201+
</variablelist>
202+
</para>
203+
<para>Caveats:</para>
204+
<para><itemizedlist>
205+
<listitem><para>the queue may grow while draining <simplesect kind="note"><para>callers should not rely on the count</para>
206+
</simplesect>
207+
</para>
208+
</listitem><listitem><para>draining twice is safe </para>
209+
</listitem></itemizedlist>
192210
</para>
193211
</detaileddescription>
194212
<inbodydescription>
195213
</inbodydescription>
196-
<location file="src/macros.h" line="60" column="6" declfile="src/macros.h" declline="60" declcolumn="6"/>
214+
<location file="src/macros.h" line="73" column="6" declfile="src/macros.h" declline="73" declcolumn="6"/>
197215
</memberdef>
198216
<memberdef kind="function" id="macros_8h_1af6f5804d6181b428b0abd148e8005ff6" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
199217
<type>struct <ref refid="structhandlers" kindref="compound">handlers</ref> *</type>
@@ -235,7 +253,7 @@
235253
</detaileddescription>
236254
<inbodydescription>
237255
</inbodydescription>
238-
<location file="src/macros.h" line="92" column="15" declfile="src/macros.h" declline="92" declcolumn="15"/>
256+
<location file="src/macros.h" line="105" column="15" declfile="src/macros.h" declline="105" declcolumn="15"/>
239257
</memberdef>
240258
</sectiondef>
241259
<briefdescription>
@@ -258,19 +276,19 @@
258276
<codeline lineno="29"><highlight class="normal"></highlight></codeline>
259277
<codeline lineno="39" refid="macros_8h_1a0a9e9396972b76c5947592479860020d" refkind="member"><highlight class="normal"></highlight><highlight class="keywordtype">void</highlight><highlight class="normal"><sp/><ref refid="macros_8h_1a0a9e9396972b76c5947592479860020d" kindref="member">flush</ref>(</highlight><highlight class="keywordtype">void</highlight><highlight class="normal">);</highlight></codeline>
260278
<codeline lineno="40"><highlight class="normal"></highlight></codeline>
261-
<codeline lineno="60" refid="macros_8h_1a8168e46e61860b969b9ee6c101bbc1c9" refkind="member"><highlight class="normal"></highlight><highlight class="keywordtype">void</highlight><highlight class="normal"><sp/><ref refid="macros_8h_1a8168e46e61860b969b9ee6c101bbc1c9" kindref="member">drain</ref>(</highlight><highlight class="keywordtype">void</highlight><highlight class="normal">);</highlight></codeline>
262-
<codeline lineno="61"><highlight class="normal"></highlight></codeline>
263-
<codeline lineno="65" refid="structhandlers" refkind="compound"><highlight class="normal"></highlight><highlight class="keyword">struct<sp/></highlight><highlight class="normal"><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>{</highlight></codeline>
264-
<codeline lineno="73" refid="structhandlers_1a996535b7879fb9713680e41af2b2791a" refkind="member"><highlight class="normal"><sp/><sp/>int<sp/>(*<ref refid="structhandlers_1a996535b7879fb9713680e41af2b2791a" kindref="member">retry</ref>)(</highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>attempt,<sp/></highlight><highlight class="keyword">const</highlight><highlight class="normal"><sp/></highlight><highlight class="keywordtype">char</highlight><highlight class="normal"><sp/>*reason);</highlight></codeline>
279+
<codeline lineno="73" refid="macros_8h_1a8168e46e61860b969b9ee6c101bbc1c9" refkind="member"><highlight class="normal"></highlight><highlight class="keywordtype">void</highlight><highlight class="normal"><sp/><ref refid="macros_8h_1a8168e46e61860b969b9ee6c101bbc1c9" kindref="member">drain</ref>(</highlight><highlight class="keywordtype">void</highlight><highlight class="normal">);</highlight></codeline>
265280
<codeline lineno="74"><highlight class="normal"></highlight></codeline>
266-
<codeline lineno="76" refid="structhandlers_1adb541f4c21033e417256fd5cab26e6b2" refkind="member"><highlight class="normal"><sp/><sp/></highlight><highlight class="keyword">struct<sp/></highlight><highlight class="normal"><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>*<ref refid="structhandlers_1adb541f4c21033e417256fd5cab26e6b2" kindref="member">fallback</ref>;</highlight></codeline>
267-
<codeline lineno="77" refid="macros_8h_1a37285142ae15ca3cdc36f3a03faeb0c9" refkind="member"><highlight class="normal">}<sp/>*<ref refid="macros_8h_1a37285142ae15ca3cdc36f3a03faeb0c9" kindref="member">registry</ref>;</highlight></codeline>
268-
<codeline lineno="78"><highlight class="normal"></highlight></codeline>
269-
<codeline lineno="83" refid="macros_8h_1a1ee636c8ea8ef76556c00290eb5a289b" refkind="member"><highlight class="normal"></highlight><highlight class="preprocessor">#define<sp/>RETRY_HOOK<sp/>registry-&gt;retry</highlight><highlight class="normal"></highlight></codeline>
270-
<codeline lineno="84"><highlight class="normal"></highlight></codeline>
271-
<codeline lineno="92" refid="macros_8h_1af6f5804d6181b428b0abd148e8005ff6" refkind="member"><highlight class="normal"></highlight><highlight class="keyword">struct<sp/></highlight><highlight class="normal"><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>*<ref refid="macros_8h_1af6f5804d6181b428b0abd148e8005ff6" kindref="member">configure</ref>(</highlight><highlight class="keyword">struct</highlight><highlight class="normal"><sp/><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>*target,<sp/></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>count);</highlight></codeline>
272-
<codeline lineno="93"><highlight class="normal"></highlight></codeline>
273-
<codeline lineno="94"><highlight class="normal"></highlight><highlight class="preprocessor">#endif<sp/></highlight><highlight class="comment">/*<sp/>__MACROS_H__<sp/>*/</highlight><highlight class="preprocessor"></highlight></codeline>
281+
<codeline lineno="78" refid="structhandlers" refkind="compound"><highlight class="normal"></highlight><highlight class="keyword">struct<sp/></highlight><highlight class="normal"><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>{</highlight></codeline>
282+
<codeline lineno="86" refid="structhandlers_1a996535b7879fb9713680e41af2b2791a" refkind="member"><highlight class="normal"><sp/><sp/>int<sp/>(*<ref refid="structhandlers_1a996535b7879fb9713680e41af2b2791a" kindref="member">retry</ref>)(</highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>attempt,<sp/></highlight><highlight class="keyword">const</highlight><highlight class="normal"><sp/></highlight><highlight class="keywordtype">char</highlight><highlight class="normal"><sp/>*reason);</highlight></codeline>
283+
<codeline lineno="87"><highlight class="normal"></highlight></codeline>
284+
<codeline lineno="89" refid="structhandlers_1adb541f4c21033e417256fd5cab26e6b2" refkind="member"><highlight class="normal"><sp/><sp/></highlight><highlight class="keyword">struct<sp/></highlight><highlight class="normal"><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>*<ref refid="structhandlers_1adb541f4c21033e417256fd5cab26e6b2" kindref="member">fallback</ref>;</highlight></codeline>
285+
<codeline lineno="90" refid="macros_8h_1a37285142ae15ca3cdc36f3a03faeb0c9" refkind="member"><highlight class="normal">}<sp/>*<ref refid="macros_8h_1a37285142ae15ca3cdc36f3a03faeb0c9" kindref="member">registry</ref>;</highlight></codeline>
286+
<codeline lineno="91"><highlight class="normal"></highlight></codeline>
287+
<codeline lineno="96" refid="macros_8h_1a1ee636c8ea8ef76556c00290eb5a289b" refkind="member"><highlight class="normal"></highlight><highlight class="preprocessor">#define<sp/>RETRY_HOOK<sp/>registry-&gt;retry</highlight><highlight class="normal"></highlight></codeline>
288+
<codeline lineno="97"><highlight class="normal"></highlight></codeline>
289+
<codeline lineno="105" refid="macros_8h_1af6f5804d6181b428b0abd148e8005ff6" refkind="member"><highlight class="normal"></highlight><highlight class="keyword">struct<sp/></highlight><highlight class="normal"><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>*<ref refid="macros_8h_1af6f5804d6181b428b0abd148e8005ff6" kindref="member">configure</ref>(</highlight><highlight class="keyword">struct</highlight><highlight class="normal"><sp/><ref refid="structhandlers" kindref="compound">handlers</ref><sp/>*target,<sp/></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>count);</highlight></codeline>
290+
<codeline lineno="106"><highlight class="normal"></highlight></codeline>
291+
<codeline lineno="107"><highlight class="normal"></highlight><highlight class="preprocessor">#endif<sp/></highlight><highlight class="comment">/*<sp/>__MACROS_H__<sp/>*/</highlight><highlight class="preprocessor"></highlight></codeline>
274292
</programlisting>
275293
<location file="src/macros.h"/>
276294
</compounddef>

‎test/fixtures/macros/xml-out/xml/structhandlers.xml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
</detaileddescription>
3838
<inbodydescription>
3939
</inbodydescription>
40-
<location file="src/macros.h" line="73" column="3" bodyfile="src/macros.h" bodystart="73" bodyend="-1"/>
40+
<location file="src/macros.h" line="86" column="3" bodyfile="src/macros.h" bodystart="86" bodyend="-1"/>
4141
</memberdef>
4242
<memberdef kind="variable" id="structhandlers_1adb541f4c21033e417256fd5cab26e6b2" prot="public" static="no" mutable="no">
4343
<type>struct <ref refid="structhandlers" kindref="compound">handlers</ref> *</type>
@@ -52,7 +52,7 @@
5252
</detaileddescription>
5353
<inbodydescription>
5454
</inbodydescription>
55-
<location file="src/macros.h" line="76" column="17" bodyfile="src/macros.h" bodystart="76" bodyend="-1"/>
55+
<location file="src/macros.h" line="89" column="17" bodyfile="src/macros.h" bodystart="89" bodyend="-1"/>
5656
</memberdef>
5757
</sectiondef>
5858
<briefdescription>
@@ -69,7 +69,7 @@
6969
</childnode>
7070
</node>
7171
</collaborationgraph>
72-
<location file="src/macros.h" line="65" column="1" bodyfile="src/macros.h" bodystart="65" bodyend="77"/>
72+
<location file="src/macros.h" line="78" column="1" bodyfile="src/macros.h" bodystart="78" bodyend="90"/>
7373
<listofallmembers>
7474
<member refid="structhandlers_1adb541f4c21033e417256fd5cab26e6b2" prot="public" virt="non-virtual"><scope>handlers</scope><name>fallback</name></member>
7575
<member refid="structhandlers_1a996535b7879fb9713680e41af2b2791a" prot="public" virt="non-virtual"><scope>handlers</scope><name>retry</name></member>

‎test/integration.test.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,9 @@ describe('integration', () => {
551551
expect(api).toContain('> [!IMPORTANT]\n> Never call this from a signal handler.');
552552
// GitHub renders the Pandoc container syntax as literal text.
553553
expect(api).not.toContain(':::');
554+
// An alert inside a list item sits at the item's content column, or it
555+
// ends the list instead of belonging to it.
556+
expect(api).toContain(' > [!NOTE]\n > callers should not rely on the count');
554557
});
555558

556559
it('renders macros by their real shape and keeps undocumented ones', async () => {

‎test/snapshots/macros/api.md‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Defined in src/macros.h:23
8080
#define RETRY_HOOK registry->retry
8181
```
8282
83-
Defined in src/macros.h:83
83+
Defined in src/macros.h:96
8484
8585
Shorthand for the registry retry hook. Its initializer references another documented symbol, so Doxygen emits a cross-reference inside the initializer.
8686
@@ -146,7 +146,7 @@ Never call this from a signal handler.
146146
void drain(void)
147147
```
148148

149-
Defined in src/macros.h:60
149+
Defined in src/macros.h:73
150150

151151
Drain the queue.
152152

@@ -166,6 +166,20 @@ Steps:
166166
1. verify
167167
1. close
168168

169+
Terms:
170+
171+
* **pending**: queued but not started
172+
* **settled**: finished either way
173+
174+
Caveats:
175+
176+
* the queue may grow while draining
177+
:::note
178+
callers should not rely on the count
179+
180+
:::
181+
* draining twice is safe
182+
169183
---
170184

171185
{#configure}
@@ -176,7 +190,7 @@ Steps:
176190
struct handlers * configure(struct handlers * target, int count)
177191
```
178192
179-
Defined in src/macros.h:92
193+
Defined in src/macros.h:105
180194
181195
Configure a handler registry.
182196
@@ -204,6 +218,6 @@ struct handlers * registry
204218

205219
Type: struct [`handlers`](handlers.md#handlers) *
206220

207-
Defined in src/macros.h:77
221+
Defined in src/macros.h:90
208222

209223
Generated by [Moxygen](https://0state.com/moxygen)

‎test/snapshots/macros/handlers.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
struct handlers
1111
```
1212
13-
Defined in src/macros.h:65
13+
Defined in src/macros.h:78
1414
1515
Registry of handler hooks.
1616
@@ -31,7 +31,7 @@ Registry of handler hooks.
3131
int(* retry)(int attempt, const char *reason)
3232
```
3333

34-
Defined in src/macros.h:73
34+
Defined in src/macros.h:86
3535

3636
Called when a retry is scheduled.
3737

@@ -57,7 +57,7 @@ struct handlers * fallback
5757
5858
Type: struct [`handlers`](#handlers) *
5959
60-
Defined in src/macros.h:76
60+
Defined in src/macros.h:89
6161
6262
The registry consulted when this one has no handler.
6363

0 commit comments

Comments
 (0)