Mercurial > projects > dil
comparison trunk/src/dil/doc/Doc.d @ 785:57ef69eced96
Added functions isCodeSection() and skipCodeSection().
Added a lot of documentation comments and revised some.
author | Aziz K?ksal <aziz.koeksal@gmail.com> |
---|---|
date | Sat, 23 Feb 2008 21:15:56 +0100 |
parents | 939097e0990f |
children | 3b34f6a95a27 |
comparison
equal
deleted
inserted
replaced
784:939097e0990f | 785:57ef69eced96 |
---|---|
10 import dil.Unicode; | 10 import dil.Unicode; |
11 import common; | 11 import common; |
12 | 12 |
13 import tango.text.Ascii : icompare; | 13 import tango.text.Ascii : icompare; |
14 | 14 |
15 /// Represents a sanitized and parsed DDoc comment. | |
15 class DDocComment | 16 class DDocComment |
16 { | 17 { |
17 Section[] sections; /// The sections of this comment. | 18 Section[] sections; /// The sections of this comment. |
18 Section summary; /// Optional summary section. | 19 Section summary; /// Optional summary section. |
19 Section description; /// Optional description section. | 20 Section description; /// Optional description section. |
35 return section; | 36 return section; |
36 } | 37 } |
37 return null; | 38 return null; |
38 } | 39 } |
39 | 40 |
40 /// Returns: true if "ditto" is the only text in this comment. | 41 /// Returns true if "ditto" is the only text in this comment. |
41 bool isDitto() | 42 bool isDitto() |
42 { | 43 { |
43 if (summary && sections.length == 1 && | 44 if (summary && sections.length == 1 && |
44 icompare(strip(summary.text), "ditto") == 0) | 45 icompare(strip(summary.text), "ditto") == 0) |
45 return true; | 46 return true; |
46 return false; | 47 return false; |
47 } | 48 } |
48 | |
49 // MacrosSection[] getMacros() | |
50 // { | |
51 // MacrosSection[] macros; | |
52 // foreach (section; sections) | |
53 // if (section.Is("macros")) | |
54 // macros ~= new MacrosSection(section.name, section.text); | |
55 // return macros; | |
56 // } | |
57 } | 49 } |
58 | 50 |
59 /// Returns a node's DDocComment. | 51 /// Returns a node's DDocComment. |
60 DDocComment getDDocComment(Node node) | 52 DDocComment getDDocComment(Node node) |
61 { | 53 { |
88 } | 80 } |
89 | 81 |
90 /// Parses a DDoc comment string. | 82 /// Parses a DDoc comment string. |
91 struct DDocParser | 83 struct DDocParser |
92 { | 84 { |
93 char* p; | 85 char* p; /// Current character pointer. |
94 char* textEnd; | 86 char* textEnd; /// Points one character past the end of the text. |
95 Section[] sections; /// Parsed sections. | 87 Section[] sections; /// Parsed sections. |
96 Section summary; /// Optional summary section. | 88 Section summary; /// Optional summary section. |
97 Section description; /// Optional description section. | 89 Section description; /// Optional description section. |
98 | 90 |
99 /// Parses the DDoc text into sections. | 91 /// Parses the DDoc text into sections. |
149 {} | 141 {} |
150 end++; | 142 end++; |
151 return makeString(begin, end); | 143 return makeString(begin, end); |
152 } | 144 } |
153 | 145 |
146 /// Separates the text between p and end | |
147 /// into a summary and description section. | |
154 void scanSummaryAndDescription(char* p, char* end) | 148 void scanSummaryAndDescription(char* p, char* end) |
155 { | 149 { |
156 assert(p <= end); | 150 assert(p <= end); |
157 char* sectionBegin = p; | 151 char* sectionBegin = p; |
158 // Search for the end of the first paragraph. | 152 // Search for the end of the first paragraph. |
159 end--; // Decrement end, so we can look ahead one character. | 153 end--; // Decrement end, so we can look ahead one character. |
160 while (p < end && !(*p == '\n' && p[1] == '\n')) | 154 while (p < end && !(*p == '\n' && p[1] == '\n')) |
161 { | 155 { |
162 // Skip over code sections. This is unlike how dmd behaves. | 156 if (isCodeSection(p, end)) |
163 if (p+2 < end && *p == '-' && p[1] == '-' && p[2] == '-') | 157 skipCodeSection(p, end); |
164 { | |
165 while (p < end && *p == '-') | |
166 p++; | |
167 p--; | |
168 while (++p < end) | |
169 if (p+2 < end && *p == '-' && p[1] == '-' && p[2] == '-') | |
170 break; | |
171 if (p >= end) | |
172 break; | |
173 } | |
174 p++; | 158 p++; |
175 } | 159 } |
176 end++; | 160 end++; |
177 if (p+1 >= end) | 161 if (p+1 >= end) |
178 p = end; | 162 p = end; |
191 sections ~= description; | 175 sections ~= description; |
192 } | 176 } |
193 } | 177 } |
194 } | 178 } |
195 | 179 |
180 /// Returns true if p points to "$(DDD)". | |
181 bool isCodeSection(char* p, char* end) | |
182 { | |
183 return p+2 < end && *p == '-' && p[1] == '-' && p[2] == '-'; | |
184 } | |
185 | |
186 /// Skips over a code section. | |
187 /// | |
188 /// Note that dmd apparently doesn't skip over code sections when | |
189 /// parsing DDoc sections. However, from experience it seems | |
190 /// to be a good idea to do that. | |
191 void skipCodeSection(ref char* p, char* end) | |
192 out { assert(p+1 == end || *p == '-'); } | |
193 body | |
194 { | |
195 assert(isCodeSection(p, end)); | |
196 | |
197 while (p < end && *p == '-') | |
198 p++; | |
199 p--; | |
200 while (++p < end) | |
201 if (p+2 < end && *p == '-' && p[1] == '-' && p[2] == '-') | |
202 break; | |
203 while (p < end && *p == '-') | |
204 p++; | |
205 p--; | |
206 } | |
207 | |
196 void skipWhitespace(ref char* p) | 208 void skipWhitespace(ref char* p) |
197 { | 209 { |
198 while (p < textEnd && (isspace(*p) || *p == '\n')) | 210 while (p < textEnd && (isspace(*p) || *p == '\n')) |
199 p++; | 211 p++; |
200 } | 212 } |
201 | 213 |
202 /// Find next "Identifier:". | 214 /// Find next "Identifier:". |
203 /// Params: | 215 /// Params: |
204 /// ident = set to the Identifier | 216 /// ident = set to the Identifier. |
205 /// bodyBegin = set to the beginning of the text body (whitespace skipped.) | 217 /// bodyBegin = set to the beginning of the text body (whitespace skipped.) |
206 /// Returns: true if found. | 218 /// Returns: true if found. |
207 bool findNextIdColon(ref char[] ident, ref char* bodyBegin) | 219 bool findNextIdColon(ref char[] ident, ref char* bodyBegin) |
208 { | 220 { |
209 while (p < textEnd) | 221 while (p < textEnd) |
210 { | 222 { |
211 skipWhitespace(p); | 223 skipWhitespace(p); |
212 if (p >= textEnd) | 224 if (p >= textEnd) |
213 break; | 225 break; |
226 if (isCodeSection(p, textEnd)) | |
227 { | |
228 skipCodeSection(p, textEnd); | |
229 p++; | |
230 continue; | |
231 } | |
214 assert(isascii(*p) || isLeadByte(*p)); | 232 assert(isascii(*p) || isLeadByte(*p)); |
215 auto idBegin = p; | 233 auto idBegin = p; |
216 if (isidbeg(*p) || isUnicodeAlpha(p, textEnd)) // IdStart | 234 if (isidbeg(*p) || isUnicodeAlpha(p, textEnd)) // IdStart |
217 { | 235 { |
218 do // IdChar* | 236 do // IdChar* |
234 } | 252 } |
235 return false; | 253 return false; |
236 } | 254 } |
237 } | 255 } |
238 | 256 |
257 /// Represents a DDoc section. | |
239 class Section | 258 class Section |
240 { | 259 { |
241 string name; | 260 string name; |
242 string text; | 261 string text; |
243 this(string name, string text) | 262 this(string name, string text) |
244 { | 263 { |
245 this.name = name; | 264 this.name = name; |
246 this.text = text; | 265 this.text = text; |
247 } | 266 } |
248 | 267 |
268 /// Case-insensitively compares the section's name with name2. | |
249 bool Is(char[] name2) | 269 bool Is(char[] name2) |
250 { | 270 { |
251 return icompare(name, name2) == 0; | 271 return icompare(name, name2) == 0; |
252 } | 272 } |
253 } | 273 } |
288 this.macroTexts[i] = idvalue.value; | 308 this.macroTexts[i] = idvalue.value; |
289 } | 309 } |
290 } | 310 } |
291 } | 311 } |
292 | 312 |
313 /// Returns true if token is a Doxygen comment. | |
293 bool isDoxygenComment(Token* token) | 314 bool isDoxygenComment(Token* token) |
294 { // Doxygen: '/+!' '/*!' '//!' | 315 { // Doxygen: '/+!' '/*!' '//!' |
295 return token.kind == TOK.Comment && token.start[2] == '!'; | 316 return token.kind == TOK.Comment && token.start[2] == '!'; |
296 } | 317 } |
297 | 318 |
319 /// Returns true if token is a DDoc comment. | |
298 bool isDDocComment(Token* token) | 320 bool isDDocComment(Token* token) |
299 { // DDOC: '/++' '/**' '///' | 321 { // DDOC: '/++' '/**' '///' |
300 return token.kind == TOK.Comment && token.start[1] == token.start[2]; | 322 return token.kind == TOK.Comment && token.start[1] == token.start[2]; |
301 } | 323 } |
302 | 324 |
303 /++ | 325 /// Returns the surrounding documentation comment tokens. |
304 Returns the surrounding documentation comment tokens. | 326 /// Params: |
305 Note: this function works correctly only if | 327 /// node = the node to find doc comments for. |
306 the source text is syntactically correct. | 328 /// isDocComment = a function predicate that checks for doc comment tokens. |
307 +/ | 329 /// Note: this function works correctly only if |
330 /// the source text is syntactically correct. | |
308 Token*[] getDocTokens(Node node, bool function(Token*) isDocComment = &isDDocComment) | 331 Token*[] getDocTokens(Node node, bool function(Token*) isDocComment = &isDDocComment) |
309 { | 332 { |
310 Token*[] comments; | 333 Token*[] comments; |
311 auto isEnumMember = node.kind == NodeKind.EnumMemberDeclaration; | 334 auto isEnumMember = node.kind == NodeKind.EnumMemberDeclaration; |
312 // Get preceding comments. | 335 // Get preceding comments. |
313 auto token = node.begin; | 336 auto token = node.begin; |
314 // Scan backwards until we hit another declaration. | 337 // Scan backwards until we hit another declaration. |
315 Loop: | 338 Loop: |
316 while (1) | 339 for (; token; token = token.prev) |
317 { | 340 { |
318 token = token.prev; | |
319 if (token.kind == TOK.LBrace || | 341 if (token.kind == TOK.LBrace || |
320 token.kind == TOK.RBrace || | 342 token.kind == TOK.RBrace || |
321 token.kind == TOK.Semicolon || | 343 token.kind == TOK.Semicolon || |
322 token.kind == TOK.HEAD || | 344 /+token.kind == TOK.HEAD ||+/ |
323 (isEnumMember && token.kind == TOK.Comma)) | 345 (isEnumMember && token.kind == TOK.Comma)) |
324 break; | 346 break; |
325 | 347 |
326 if (token.kind == TOK.Comment) | 348 if (token.kind == TOK.Comment) |
327 { // Check that this comment doesn't belong to the previous declaration. | 349 { // Check that this comment doesn't belong to the previous declaration. |
377 // Stdout.formatln("→{}←", result); | 399 // Stdout.formatln("→{}←", result); |
378 return result[0..$-1]; // Remove \n or ' ' | 400 return result[0..$-1]; // Remove \n or ' ' |
379 } | 401 } |
380 | 402 |
381 /// Sanitizes a DDoc comment string. | 403 /// Sanitizes a DDoc comment string. |
404 /// | |
382 /// Leading "commentChar"s are removed from the lines. | 405 /// Leading "commentChar"s are removed from the lines. |
383 /// The various newline types are converted to '\n'. | 406 /// The various newline types are converted to '\n'. |
384 /// Params: | 407 /// Params: |
385 /// comment = the string to be sanitized. | 408 /// comment = the string to be sanitized. |
386 /// commentChar = '/', '+', or '*' | 409 /// commentChar = '/', '+', or '*' |