@@ -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 ;
1919import std.stdio ;
2020
21+ import dmd.cli;
22+
2123struct 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 ~= " \n SRC_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)
172176auto 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