Skip to content

Commit a20d6e7

Browse files
committed
docs: config options reference extension
1 parent eea9026 commit a20d6e7

File tree

5 files changed

+230
-49
lines changed

5 files changed

+230
-49
lines changed

docs/antora-playbook.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ asciidoc:
5151
- ./extensions/mrdocs-demos.js
5252
- ./extensions/mrdocs-releases.js
5353
- ./extensions/c-preprocessor.js
54+
- ./extensions/config-options-reference.js
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*
2+
Copyright (c) 2024 Alan de Freitas ([email protected])
3+
4+
Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
7+
Official repository: https://github.com/cppalliance/mrdocs
8+
*/
9+
10+
function toSnakeCase(str) {
11+
return str.toLowerCase().replace(/ /g, '_').replace(/[^a-z0-9_]/g, '');
12+
}
13+
14+
function escapeHtml(str)
15+
{
16+
return str
17+
.replace(/&/g, "&")
18+
.replace(/</g, "&lt;")
19+
.replace(/>/g, "&gt;")
20+
.replace(/"/g, "&quot;")
21+
.replace(/'/g, "&#039;");
22+
}
23+
24+
function replaceCodeTags(str) {
25+
return str.replace(/`([^`\n]+)`/g, '<code>$1</code>')
26+
}
27+
28+
function toTypeStr(type) {
29+
if (type === undefined) {
30+
return 'object'
31+
}
32+
if (type === 'string') {
33+
return 'string'
34+
}
35+
if (type === 'int') {
36+
return 'integer'
37+
}
38+
if (type === 'unsigned') {
39+
return 'unsigned integer'
40+
}
41+
if (type === 'bool') {
42+
return 'boolean'
43+
}
44+
if (type === 'path') {
45+
return 'path'
46+
}
47+
if (type === 'dir-path') {
48+
return 'directory path'
49+
}
50+
if (type === 'file-path') {
51+
return 'file path'
52+
}
53+
if (type.startsWith('list<')) {
54+
return 'list of ' + toTypeStr(type.substring(5, type.length - 1)) + 's'
55+
}
56+
return type
57+
}
58+
59+
function toDefaultValueStr(value) {
60+
if (value === undefined) {
61+
return ''
62+
}
63+
let valueIsArray = Array.isArray(value)
64+
if (valueIsArray) {
65+
let valueStrs = value.map(v => toDefaultValueStr(v))
66+
return `[${valueStrs.join(', ')}]`
67+
}
68+
let valueIsObject = typeof value === 'object'
69+
if (valueIsObject) {
70+
let valueStrs = Object.keys(value).map(k => `${k}: ${toDefaultValueStr(value[k])}`)
71+
return `{${valueStrs.join(', ')}}`
72+
}
73+
let valueIsBool = typeof value === 'boolean'
74+
if (valueIsBool) {
75+
return `<span style="color: darkblue;">${value}</span>`
76+
}
77+
let valueIsString = typeof value === 'string'
78+
if (valueIsString) {
79+
if (value === '') {
80+
return ''
81+
}
82+
return `<span style="color: darkgreen;">"${escapeHtml(value)}"</span>`
83+
}
84+
let valueIsNumber = typeof value === 'number'
85+
if (valueIsNumber) {
86+
return `<span style="color: darkblue;">${value}</span>`
87+
}
88+
return `${value}`
89+
90+
}
91+
92+
function pushOptionBlocks(options, block, parents = []) {
93+
block.lines.push('<table class="tableblock frame-all grid-all stretch">')
94+
block.lines.push('<colgroup>')
95+
block.lines.push('<col style="width: 23.3333%;">')
96+
block.lines.push('<col style="width: 46.6667%;">')
97+
block.lines.push('<col style="width: 30%;">')
98+
block.lines.push('</colgroup>')
99+
block.lines.push('<thead>')
100+
block.lines.push('<tr>')
101+
block.lines.push('<th class="tableblock halign-left valign-top">Name</th>')
102+
block.lines.push('<th class="tableblock halign-left valign-top">Description</th>')
103+
block.lines.push('<th class="tableblock halign-left valign-top">Default</th>')
104+
block.lines.push('</tr>')
105+
block.lines.push('</thead>')
106+
block.lines.push('<tbody>')
107+
for (let option of options) {
108+
let optionName = [...parents, option.name].join('.')
109+
block.lines.push('<tr>')
110+
block.lines.push(`<td class="tableblock halign-left valign-top">`)
111+
block.lines.push(`<code style="color: darkblue">${optionName}</code>`)
112+
block.lines.push(`<br/>`)
113+
block.lines.push(`<span style="color: darkgreen;">(${toTypeStr(option.type)})</span>`)
114+
let observations = []
115+
if (option.required) {
116+
observations.push('Required')
117+
}
118+
if (option['command-line-only']) {
119+
observations.push('Command line only')
120+
}
121+
if (observations.length !== 0) {
122+
block.lines.push(`<br/><br/>`)
123+
let observationsStr = observations.join(', ')
124+
block.lines.push(`<span style="color: orangered;">(${observationsStr})</span>`)
125+
}
126+
block.lines.push(`</td>`)
127+
block.lines.push(`<td class="tableblock halign-left valign-top">${option.brief}</td>`)
128+
block.lines.push(`<td class="tableblock halign-left valign-top">${toDefaultValueStr(option.default)}</td>`)
129+
block.lines.push('</tr>')
130+
}
131+
block.lines.push('</tbody>')
132+
block.lines.push('</table>')
133+
134+
// Option details
135+
for (let option of options) {
136+
let optionName = [...parents, option.name].join('.')
137+
block.lines.push(`<div class="paragraph"><p><b><code style="color: darkblue">${optionName}</code></b></p></div>`)
138+
block.lines.push(`<div class="paragraph"><p><i>${option.brief}</i></p></div>`)
139+
if (option.details) {
140+
block.lines.push(`<div class="paragraph"><p>${replaceCodeTags(escapeHtml(option.details))}</p></div>`)
141+
}
142+
block.lines.push(`<div class="paragraph"><p>`)
143+
block.lines.push(`<div class="ulist">`)
144+
block.lines.push(`<ul>`)
145+
if (option.type) {
146+
block.lines.push(`<li>Type: ${toTypeStr(option.type)}</li>`)
147+
} else {
148+
block.lines.push(`<li>Type: object (See below)</li>`)
149+
}
150+
if (option.required) {
151+
block.lines.push(`<li><span style="color: orangered;">Required</span></li>`)
152+
}
153+
if (option['command-line-only']) {
154+
block.lines.push(`<li>Command line only</li>`)
155+
}
156+
if (option['must-exits']) {
157+
block.lines.push(`<li>The path must exist</li>`)
158+
}
159+
if (option.default !== undefined) {
160+
block.lines.push(`<li>Default value: ${toDefaultValueStr(option.default)}</li>`)
161+
}
162+
if (option.type === 'enum') {
163+
block.lines.push(`<li>Allowed values: ${toDefaultValueStr(option.values)}</li>`)
164+
}
165+
if (option['command-line-sink']) {
166+
block.lines.push(`<li>This command is a command line sink. Any command line argument that is not recognized by the parser will be passed to this command.</li>`)
167+
}
168+
if (option['min-value'] || option.type === 'unsigned') {
169+
block.lines.push(`<li>Minimum value: ${toDefaultValueStr(option['min-value'] || 0)}</li>`)
170+
}
171+
if (option['max-value']) {
172+
block.lines.push(`<li>Maximum value: ${toDefaultValueStr(option['max-value'])}</li>`)
173+
}
174+
if (option.relativeto) {
175+
block.lines.push(`<li>Relative paths are relative to: ${toDefaultValueStr(escapeHtml(option.relativeto))}</li>`)
176+
}
177+
block.lines.push(`</ul>`)
178+
block.lines.push(`</div>`)
179+
block.lines.push(`</p></div>`)
180+
}
181+
182+
// Iterate the options that have suboptions
183+
for (let option of options) {
184+
if (!option.options) {
185+
continue
186+
}
187+
block.lines.push(`<div class="paragraph"><p><b><code>${option.name}</code> suboptions</b></p></div>`)
188+
pushOptionBlocks(option.options, block, [...parents, option.name]);
189+
}
190+
}
191+
192+
module.exports = function (registry) {
193+
// Make sure registry is defined
194+
if (!registry) {
195+
throw new Error('registry must be defined');
196+
}
197+
198+
registry.block('config-options-reference', function () {
199+
const self = this
200+
self.onContext('example')
201+
let blocks = []
202+
self.process((parent, reader, attrs) => {
203+
let level = attrs.level || 3
204+
let code = reader.getLines().join('\n')
205+
let categories = JSON.parse(code)
206+
let block = self.$create_pass_block(parent, '', Opal.hash(attrs))
207+
for (let category of categories) {
208+
block.lines.push('<div class="sect2">')
209+
let snake_case = toSnakeCase(category.category)
210+
block.lines.push(`<h${level} id="_${snake_case}_options_reference">`)
211+
block.lines.push(`<a class="anchor" href="#_${snake_case}_options_reference"></a>`)
212+
block.lines.push(category.category)
213+
block.lines.push(`</h${level}>`)
214+
if (category.brief) {
215+
block.lines.push(`<div class="paragraph"><p><i>${category.brief}</i></p></div>`)
216+
}
217+
if (category.details) {
218+
block.lines.push(`<div class="paragraph"><p>${replaceCodeTags(escapeHtml(category.details))}</p></div>`)
219+
}
220+
pushOptionBlocks(category.options, block);
221+
block.lines.push(`</div>`)
222+
}
223+
return block
224+
})
225+
})
226+
}

docs/modules/ROOT/pages/config-file.adoc

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -240,55 +240,9 @@ class B { /* see below */ };
240240
[#config-options-reference]
241241
== Reference
242242

243-
=== Command Line Options
244-
245-
The following options can be used to control the general behavior of MrDocs and can only be provided via the command line:
246-
247-
[c-preprocessor]
248-
====
249-
250-
[cols="1,2"]
251-
|===
252-
| Name | Description
253-
254-
#define CMDLINE_OPTION(Name, Kebab, Desc) | `pass:[Kebab]` | Desc
255-
include::partial$ConfigOptions.inc[]
256-
257-
|===
258-
====
259-
260-
* Any argument provided to MrDocs without a key is treated as one path in `inputs`.
261-
These can be paths to the configuration file `config`, the `compilation-database` (compilation database or build scripts), or the `source-root` directory.
262-
See the <<common-options>> section for more information on these options.
263-
264-
* The `config` option can be used to explicitly specify a configuration file.
265-
Any path in `inputs` whose filename is `mrdocs.yml` will also be used as the configuration file.
266-
If no configuration file is provided via `inputs` or `config`, MrDocs will attempt to find `mrdocs.yml` in the current directory.
267-
268-
[IMPORTANT]
269-
====
270-
The `config` path is interpreted relative to the current working directory by MrDocs.
271-
All other path options are interpreted relative to the `mrdocs.yml` configuration file.
272-
====
273-
274-
[#common-options]
275-
=== Common Options
276-
277243
The following options can be defined both in the configuration file and on the command line, where the command line options always take precedence.
278244

279-
[c-preprocessor]
245+
[config-options-reference,level=3]
280246
====
281-
282-
|===
283-
| Name | Description
284-
285-
#define COMMON_OPTION(Name, Kebab, Desc) | `pass:[Kebab]` | Desc
286-
include::partial$ConfigOptions.inc[]
287-
288-
|===
247+
include::partial$ConfigOptions.json[]
289248
====
290-
291-
// == Custom commands / Command Aliases
292-
// ALIASES += sideeffect="\par Side Effects:^^"
293-
// ALIASES += l{1}="\ref \1"
294-
// See \l{SomeClass} for more information.

docs/modules/ROOT/partials/ConfigOptions.inc

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../../src/lib/Lib/ConfigOptions.json

0 commit comments

Comments
 (0)