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 '*'