Skip to content

Commit 1cf9831

Browse files
committed
Generate CLI pages automatically
1 parent df7a1b8 commit 1cf9831

1 file changed

Lines changed: 103 additions & 11 deletions

File tree

ddoc_preprocessor.d

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import std.algorithm, std.array, std.ascii, std.conv, std.file, std.format, std.
1818
std.meta, std.path, std.range, std.string, std.typecons;
1919
import std.stdio;
2020

21+
import dmd.cli;
22+
2123
struct Config
2224
{
2325
string dmdBinPath = "dmd";
@@ -51,6 +53,7 @@ All unknown options are passed to the compiler.
5153
// transform and extend the ddoc page
5254
text = genGrammar(text);
5355
text = genHeader(text);
56+
text = genSwitches(text);
5457

5558
// inject custom, "dynamic" macros
5659
text ~= "\nSRC_FILENAME=%s\n".format(inputFile.buildNormalizedPath);
@@ -72,15 +75,16 @@ auto compile(R)(R buffer, string[] arguments)
7275
return wait(pipes.pid);
7376
}
7477

78+
enum indent = " ";
79+
7580
// replaces the content of a DDoc macro call
76-
auto updateDdocTag(string fileText, string ddocKey, string newContent)
81+
auto updateDdocTag(string fileText, string ddocKey, string content)
7782
{
83+
auto newContent = ddocKey ~ "\n" ~ indent ~ content ~ ")";
7884
auto pos = fileText.representation.countUntil(ddocKey);
7985
if (pos < 0)
8086
return fileText;
8187

82-
newContent ~= ")";
83-
8488
const ddocStartLength = ddocKey.representation.until('(', No.openRight).count;
8589
auto len = fileText[pos .. $].representation.drop(ddocStartLength).untilClosingParentheses.walkLength;
8690
return fileText.replace(fileText[pos .. pos + len + ddocStartLength + 1], newContent);
@@ -172,27 +176,26 @@ auto escapeDdoc(string s)
172176
auto genHeader(string fileText)
173177
{
174178
enum ddocKey = "$(HEADERNAV_TOC";
175-
auto newContent = ddocKey ~ "\n";
176-
enum indent = " ";
179+
string content;
177180
if (fileText.canFind(ddocKey))
178181
{
179182
foreach (entry; fileText.parseToc)
180183
{
181184
if (entry.children)
182185
{
183-
newContent ~= "%s$(HEADERNAV_SUBITEMS %s, %s,\n".format(indent, entry.main.id, entry.main.name.escapeDdoc);
186+
content ~= "%s$(HEADERNAV_SUBITEMS %s, %s,\n".format(indent, entry.main.id, entry.main.name.escapeDdoc);
184187
foreach (child; entry.children)
185-
newContent ~= "%s$(HEADERNAV_ITEM %s, %s)\n".format(indent.repeat(2).joiner, child.id, child.name.escapeDdoc);
186-
newContent ~= indent;
187-
newContent ~= ")\n";
188+
content ~= "%s$(HEADERNAV_ITEM %s, %s)\n".format(indent.repeat(2).joiner, child.id, child.name.escapeDdoc);
189+
content ~= indent;
190+
content ~= ")\n";
188191
}
189192
else
190193
{
191-
newContent ~= "%s$(HEADERNAV_ITEM %s, %s)\n".format(indent, entry.main.id, entry.main.name.escapeDdoc);
194+
content ~= "%s$(HEADERNAV_ITEM %s, %s)\n".format(indent, entry.main.id, entry.main.name.escapeDdoc);
192195
}
193196
}
194197
}
195-
return updateDdocTag(fileText, ddocKey, newContent);
198+
return updateDdocTag(fileText, ddocKey, content);
196199
}
197200

198201
// parse the menu from the Ddoc file
@@ -253,3 +256,92 @@ auto genGrammar(string fileText)
253256
return fileText;
254257
}
255258

259+
// generate CLI documentation
260+
auto genSwitches(string fileText)
261+
{
262+
enum ddocKey = "$(CLI_SWITCHES";
263+
string content;
264+
foreach (option; Usage.options)
265+
{
266+
string flag = option.flag;
267+
string helpText = option.helpText;
268+
269+
// capitalize the first letter
270+
helpText = helpText.capitalize.to!string;
271+
272+
highlightSpecialWords(flag, helpText);
273+
auto flagEndPos = flag.representation.countUntil("=", "$(", "<");
274+
string switchName;
275+
if (flagEndPos < 0)
276+
switchName = "$(SWNAME -%s)".format(flag);
277+
else
278+
switchName = "$(SWNAME -%s)%s".format(flag[0..flagEndPos], flag[flagEndPos..$]);
279+
280+
auto currentFlag = "$(SWITCH %s,\n
281+
%s
282+
)".format(switchName, helpText);
283+
284+
with(TargetOS)
285+
switch(option.os)
286+
{
287+
case windows:
288+
currentFlag = text("$(WINDOWS ", currentFlag, ")");
289+
break;
290+
case linux:
291+
case macOS:
292+
case freeBSD:
293+
case solaris:
294+
case dragonFlyBSD:
295+
currentFlag = text("$(UNIX ", currentFlag, ")");
296+
break;
297+
case all:
298+
default:
299+
break;
300+
}
301+
content ~= currentFlag;
302+
}
303+
return updateDdocTag(fileText, ddocKey, content);
304+
}
305+
306+
auto italic(string w)
307+
{
308+
return "$(I %s )".format(w);
309+
}
310+
311+
312+
// capitalize the first letter
313+
auto capitalize(string w)
314+
{
315+
import std.range, std.uni;
316+
return w.take(1).asUpperCase.chain(w.dropOne);
317+
}
318+
319+
private void highlightSpecialWords(ref string flag, ref string helpText)
320+
{
321+
if (flag.canFind("<", "[") && flag.canFind(">", "]"))
322+
{
323+
string specialWord;
324+
325+
// detect special words in <...> and highlight them
326+
static foreach (t; [["<", ">"], ["[", "]"]])
327+
{
328+
if (flag.canFind(t[0]))
329+
{
330+
specialWord = flag.findSplit(t[0])[2].until(t[1]).to!string;
331+
// keep []
332+
auto replaceWord = t[0] == "<" ? t[0] ~ specialWord ~ t[1] : specialWord;
333+
flag = flag.replace(replaceWord, specialWord.italic);
334+
}
335+
}
336+
337+
// highlight individual words in the description
338+
helpText = helpText
339+
.splitter(" ")
340+
.map!((w){
341+
auto wPlain = w.filter!(c => !c.among('<', '>', '`', '\'')).to!string;
342+
return wPlain == specialWord ? wPlain.italic: w;
343+
})
344+
.joiner(" ")
345+
.to!string;
346+
}
347+
}

0 commit comments

Comments
 (0)