@@ -14,7 +14,9 @@ Example usage:
14
14
15
15
Author: Sebastian Wilzbach
16
16
*/
17
- import std.stdio ;
17
+ import std.algorithm , std.array , std.ascii , std.conv , std.file , std.functional ,
18
+ std.meta , std.path , std.range , std.string , std.typecons ;
19
+ import std.stdio : writeln, writefln;
18
20
19
21
struct Config
20
22
{
@@ -45,6 +47,10 @@ int main(string[] args)
45
47
46
48
import std.file : readText;
47
49
auto text = args[$ - 1 ].readText;
50
+
51
+ // transform and extend the ddoc page
52
+ text = genHeader(text);
53
+
48
54
return compile (text, args[1 .. $ - 1 ]);
49
55
}
50
56
@@ -58,3 +64,117 @@ auto compile(R)(R buffer, string[] arguments)
58
64
pipes.stdin.close;
59
65
return wait (pipes.pid);
60
66
}
67
+
68
+ // replaces the content of a DDoc macro call
69
+ auto updateDdocTag (string fileText, string ddocKey, string newContent)
70
+ {
71
+ auto pos = fileText.representation.countUntil(ddocKey);
72
+ if (pos < 0 )
73
+ return fileText;
74
+ const ddocStartLength = ddocKey.representation.until(' (' , No.openRight).count;
75
+ auto len = fileText[pos .. $].representation.drop(ddocStartLength).untilClosingParentheses.walkLength;
76
+ return fileText.replace(fileText[pos .. pos + len + ddocStartLength + 1 ], newContent);
77
+ }
78
+
79
+ // a range until the next ')', nested () are ignored
80
+ auto untilClosingParentheses (R)(R rs)
81
+ {
82
+ return rs.cumulativeFold! ((count, r){
83
+ switch (r)
84
+ {
85
+ case ' (' :
86
+ count++ ;
87
+ break ;
88
+ case ' )' :
89
+ count-- ;
90
+ break ;
91
+ default :
92
+ }
93
+ return count;
94
+ })(1 ).zip(rs).until! (e => e[0 ] == 0 ).map! (e => e[1 ]);
95
+ }
96
+
97
+ unittest
98
+ {
99
+ import std.algorithm.comparison : equal;
100
+ assert (" aa $(foo $(bar)foobar)" .untilClosingParentheses.equal(" aa $(foo $(bar)foobar)" ));
101
+ assert (" $(FOO a, b, $(ARGS e, f)))" .untilClosingParentheses.equal(" $(FOO a, b, $(ARGS e, f))" ));
102
+ }
103
+
104
+ // parse the ddoc file for H2 and H3 items
105
+ // H3 items are listed as subitems
106
+ auto parseToc (string text)
107
+ {
108
+ alias TocEntry = Tuple ! (string , " id" , string , " name" );
109
+ alias TocTopEntry = Tuple ! (TocEntry, " main" , TocEntry[], " children" );
110
+ TocTopEntry[] toc;
111
+
112
+ bool isH2 = true ;
113
+ void append (string id, string name)
114
+ {
115
+ auto entry = TocEntry(id, name);
116
+ if (isH2)
117
+ toc ~= TocTopEntry(entry, null );
118
+ else
119
+ toc.back.children ~= entry;
120
+ }
121
+ while (! text.empty)
122
+ {
123
+ enum needles = AliasSeq! (" $(H2 " , " $(SECTION2 " , " $(H3" , " $(SECTION3" );
124
+ auto res = text.find(needles);
125
+ if (res[0 ].empty)
126
+ break ;
127
+
128
+ isH2 = res[1 ] <= 2 ;
129
+ text = res[0 ].drop(needles.only[res[1 ] - 1 ].length);
130
+ text.skipOver! isWhite;
131
+
132
+ enum gname = " $(GNAME " ;
133
+ enum lNameNeedles = AliasSeq! (" $(LNAME2" , " $(LEGACY_LNAME2" );
134
+ if (text.startsWith(gname))
135
+ {
136
+ auto name = text.drop(gname.length).untilClosingParentheses.to! string .strip;
137
+ append(name, name);
138
+ }
139
+ else if (auto idx = text.startsWith(lNameNeedles))
140
+ {
141
+ auto arr = text.drop(lNameNeedles.only[idx - 1 ].length).splitter(" ," );
142
+ if (idx == 2 )
143
+ arr.popFront;
144
+ append(arr.front.strip, arr.dropOne.joiner(" ," ).untilClosingParentheses.to! string .strip);
145
+ }
146
+ }
147
+ return toc;
148
+ }
149
+
150
+ // Ddoc splits arguments by commas
151
+ auto escapeDdoc (string s)
152
+ {
153
+ return s.replace(" ," , " $(COMMA)" );
154
+ }
155
+
156
+ // generated a SPEC_HEADERNAV_TOC Ddoc macro with the parsed H2/H3 entries
157
+ auto genHeader (string fileText)
158
+ {
159
+ enum ddocKey = " $(SPEC_HEADERNAV_TOC" ;
160
+ auto newContent = ddocKey ~ " \n " ;
161
+ enum indent = " " ;
162
+ foreach (entry; fileText.parseToc)
163
+ {
164
+ if (entry.children)
165
+ {
166
+ newContent ~= " %s$(SPEC_HEADERNAV_SUBITEMS %s, %s,\n " .format(indent, entry.main.id, entry.main.name.escapeDdoc);
167
+ foreach (child; entry.children)
168
+ newContent ~= " %s$(SPEC_HEADERNAV_ITEM %s, %s)\n " .format(indent.repeat(2 ).joiner, child.id, child.name.escapeDdoc);
169
+ newContent ~= indent;
170
+ newContent ~= " )\n " ;
171
+ }
172
+ else
173
+ {
174
+ newContent ~= " %s$(SPEC_HEADERNAV_ITEM %s, %s)\n " .format(indent, entry.main.id, entry.main.name.escapeDdoc);
175
+ }
176
+ }
177
+ newContent ~= " )" ;
178
+ return updateDdocTag (fileText, ddocKey, newContent);
179
+ }
180
+
0 commit comments