Mercurial > projects > ldc
comparison dmd/doc.c @ 1:c53b6e3fe49a trunk
[svn r5] Initial commit. Most things are very rough.
author | lindquist |
---|---|
date | Sat, 01 Sep 2007 21:43:27 +0200 |
parents | |
children | a7dfa0ed966c |
comparison
equal
deleted
inserted
replaced
0:a9e71648e74d | 1:c53b6e3fe49a |
---|---|
1 | |
2 // Compiler implementation of the D programming language | |
3 // Copyright (c) 1999-2006 by Digital Mars | |
4 // All Rights Reserved | |
5 // written by Walter Bright | |
6 // http://www.digitalmars.com | |
7 // License for redistribution is by either the Artistic License | |
8 // in artistic.txt, or the GNU General Public License in gnu.txt. | |
9 // See the included readme.txt for details. | |
10 | |
11 // This implements the Ddoc capability. | |
12 | |
13 #include <stdio.h> | |
14 #include <string.h> | |
15 #include <time.h> | |
16 #include <ctype.h> | |
17 #include <assert.h> | |
18 | |
19 #if IN_GCC || IN_LLVM | |
20 #include "mem.h" | |
21 #else | |
22 #if _WIN32 | |
23 #include "..\root\mem.h" | |
24 #elif linux | |
25 #include "../root/mem.h" | |
26 #else | |
27 #error "fix this" | |
28 #endif | |
29 #endif | |
30 | |
31 #include "root.h" | |
32 | |
33 #include "mars.h" | |
34 #include "dsymbol.h" | |
35 #include "macro.h" | |
36 #include "template.h" | |
37 #include "lexer.h" | |
38 #include "aggregate.h" | |
39 #include "declaration.h" | |
40 #include "enum.h" | |
41 #include "id.h" | |
42 #include "module.h" | |
43 #include "scope.h" | |
44 #include "hdrgen.h" | |
45 #include "doc.h" | |
46 #include "mtype.h" | |
47 | |
48 struct Escape | |
49 { | |
50 char *strings[256]; | |
51 | |
52 static char *escapeChar(unsigned c); | |
53 }; | |
54 | |
55 struct Section | |
56 { | |
57 unsigned char *name; | |
58 unsigned namelen; | |
59 | |
60 unsigned char *body; | |
61 unsigned bodylen; | |
62 | |
63 int nooutput; | |
64 | |
65 virtual void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); | |
66 }; | |
67 | |
68 struct ParamSection : Section | |
69 { | |
70 void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); | |
71 }; | |
72 | |
73 struct MacroSection : Section | |
74 { | |
75 void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf); | |
76 }; | |
77 | |
78 struct DocComment | |
79 { | |
80 Array sections; // Section*[] | |
81 | |
82 Section *summary; | |
83 Section *copyright; | |
84 Section *macros; | |
85 Macro **pmacrotable; | |
86 Escape **pescapetable; | |
87 | |
88 DocComment(); | |
89 | |
90 static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment); | |
91 static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen); | |
92 static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen); | |
93 | |
94 void parseSections(unsigned char *comment); | |
95 void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf); | |
96 }; | |
97 | |
98 | |
99 int cmp(char *stringz, void *s, size_t slen); | |
100 int icmp(char *stringz, void *s, size_t slen); | |
101 int isDitto(unsigned char *comment); | |
102 unsigned char *skipwhitespace(unsigned char *p); | |
103 unsigned skiptoident(OutBuffer *buf, unsigned i); | |
104 unsigned skippastident(OutBuffer *buf, unsigned i); | |
105 void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); | |
106 void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); | |
107 void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); | |
108 Argument *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len); | |
109 | |
110 static unsigned char ddoc_default[] = "\ | |
111 DDOC = <html><head>\n\ | |
112 <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\ | |
113 <title>$(TITLE)</title>\n\ | |
114 </head><body>\n\ | |
115 <h1>$(TITLE)</h1>\n\ | |
116 $(BODY)\n\ | |
117 <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/ddoc.html, Ddoc). $(COPYRIGHT))\n\ | |
118 </body></html>\n\ | |
119 \n\ | |
120 B = <b>$0</b>\n\ | |
121 I = <i>$0</i>\n\ | |
122 U = <u>$0</u>\n\ | |
123 P = <p>$0</p>\n\ | |
124 DL = <dl>$0</dl>\n\ | |
125 DT = <dt>$0</dt>\n\ | |
126 DD = <dd>$0</dd>\n\ | |
127 TABLE = <table>$0</table>\n\ | |
128 TR = <tr>$0</tr>\n\ | |
129 TH = <th>$0</th>\n\ | |
130 TD = <td>$0</td>\n\ | |
131 OL = <ol>$0</ol>\n\ | |
132 UL = <ul>$0</ul>\n\ | |
133 LI = <li>$0</li>\n\ | |
134 BIG = <big>$0</big>\n\ | |
135 SMALL = <small>$0</small>\n\ | |
136 BR = <br>\n\ | |
137 LINK = <a href=\"$0\">$0</a>\n\ | |
138 LINK2 = <a href=\"$1\">$+</a>\n\ | |
139 \n\ | |
140 RED = <font color=red>$0</font>\n\ | |
141 BLUE = <font color=blue>$0</font>\n\ | |
142 GREEN = <font color=green>$0</font>\n\ | |
143 YELLOW =<font color=yellow>$0</font>\n\ | |
144 BLACK = <font color=black>$0</font>\n\ | |
145 WHITE = <font color=white>$0</font>\n\ | |
146 \n\ | |
147 D_CODE = <pre class=\"d_code\">$0</pre>\n\ | |
148 D_COMMENT = $(GREEN $0)\n\ | |
149 D_STRING = $(RED $0)\n\ | |
150 D_KEYWORD = $(BLUE $0)\n\ | |
151 D_PSYMBOL = $(U $0)\n\ | |
152 D_PARAM = $(I $0)\n\ | |
153 \n\ | |
154 DDOC_COMMENT = <!-- $0 -->\n\ | |
155 DDOC_DECL = $(DT $(BIG $0))\n\ | |
156 DDOC_DECL_DD = $(DD $0)\n\ | |
157 DDOC_DITTO = $(BR)$0\n\ | |
158 DDOC_SECTIONS = $0\n\ | |
159 DDOC_SUMMARY = $0$(BR)$(BR)\n\ | |
160 DDOC_DESCRIPTION = $0$(BR)$(BR)\n\ | |
161 DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\ | |
162 DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\ | |
163 DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\ | |
164 DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\ | |
165 DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\ | |
166 DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\ | |
167 DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\ | |
168 DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\ | |
169 DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\ | |
170 DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\ | |
171 DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\ | |
172 DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\ | |
173 DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\ | |
174 DDOC_SECTION_H = $(B $0)$(BR)\n\ | |
175 DDOC_SECTION = $0$(BR)$(BR)\n\ | |
176 DDOC_MEMBERS = $(DL $0)\n\ | |
177 DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\ | |
178 DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\ | |
179 DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\ | |
180 DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\ | |
181 DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\ | |
182 DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\ | |
183 DDOC_PARAM_ROW = $(TR $0)\n\ | |
184 DDOC_PARAM_ID = $(TD $0)\n\ | |
185 DDOC_PARAM_DESC = $(TD $0)\n\ | |
186 DDOC_BLANKLINE = $(BR)$(BR)\n\ | |
187 \n\ | |
188 DDOC_PSYMBOL = $(U $0)\n\ | |
189 DDOC_KEYWORD = $(B $0)\n\ | |
190 DDOC_PARAM = $(I $0)\n\ | |
191 \n\ | |
192 ESCAPES = /</</\n\ | |
193 />/>/\n\ | |
194 /&/&/\n\ | |
195 "; | |
196 | |
197 static char ddoc_decl_s[] = "$(DDOC_DECL "; | |
198 static char ddoc_decl_e[] = ")\n"; | |
199 | |
200 static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD "; | |
201 static char ddoc_decl_dd_e[] = ")\n"; | |
202 | |
203 | |
204 /**************************************************** | |
205 */ | |
206 | |
207 void Module::gendocfile() | |
208 { | |
209 static OutBuffer mbuf; | |
210 static int mbuf_done; | |
211 | |
212 OutBuffer buf; | |
213 | |
214 //printf("Module::gendocfile()\n"); | |
215 | |
216 if (!mbuf_done) // if not already read the ddoc files | |
217 { mbuf_done = 1; | |
218 | |
219 // Use our internal default | |
220 mbuf.write(ddoc_default, sizeof(ddoc_default) - 1); | |
221 | |
222 // Override with DDOCFILE specified in the sc.ini file | |
223 char *p = getenv("DDOCFILE"); | |
224 if (p) | |
225 global.params.ddocfiles->shift(p); | |
226 | |
227 // Override with the ddoc macro files from the command line | |
228 for (int i = 0; i < global.params.ddocfiles->dim; i++) | |
229 { | |
230 FileName f((char *)global.params.ddocfiles->data[i], 0); | |
231 File file(&f); | |
232 file.readv(); | |
233 // BUG: convert file contents to UTF-8 before use | |
234 | |
235 //printf("file: '%.*s'\n", file.len, file.buffer); | |
236 mbuf.write(file.buffer, file.len); | |
237 } | |
238 } | |
239 DocComment::parseMacros(&escapetable, ¯otable, mbuf.data, mbuf.offset); | |
240 | |
241 Scope *sc = Scope::createGlobal(this); // create root scope | |
242 sc->docbuf = &buf; | |
243 | |
244 DocComment *dc = DocComment::parse(sc, this, comment); | |
245 dc->pmacrotable = ¯otable; | |
246 dc->pescapetable = &escapetable; | |
247 | |
248 // Generate predefined macros | |
249 | |
250 // Set the title to be the name of the module | |
251 { char *p = toPrettyChars(); | |
252 Macro::define(¯otable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p)); | |
253 } | |
254 | |
255 time_t t; | |
256 time(&t); | |
257 char *p = ctime(&t); | |
258 p = mem.strdup(p); | |
259 Macro::define(¯otable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p)); | |
260 Macro::define(¯otable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4); | |
261 | |
262 char *docfilename = docfile->toChars(); | |
263 Macro::define(¯otable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename)); | |
264 | |
265 if (dc->copyright) | |
266 { | |
267 dc->copyright->nooutput = 1; | |
268 Macro::define(¯otable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); | |
269 } | |
270 | |
271 buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars()); | |
272 | |
273 if (isDocFile) | |
274 { | |
275 size_t commentlen = strlen((char *)comment); | |
276 if (dc->macros) | |
277 { | |
278 commentlen = dc->macros->name - comment; | |
279 dc->macros->write(dc, sc, this, sc->docbuf); | |
280 } | |
281 sc->docbuf->write(comment, commentlen); | |
282 highlightText(NULL, this, sc->docbuf, 0); | |
283 } | |
284 else | |
285 { | |
286 | |
287 dc->writeSections(sc, this, sc->docbuf); | |
288 emitMemberComments(sc); | |
289 } | |
290 | |
291 //printf("BODY= '%.*s'\n", buf.offset, buf.data); | |
292 Macro::define(¯otable, (unsigned char *)"BODY", 4, buf.data, buf.offset); | |
293 | |
294 OutBuffer buf2; | |
295 buf2.writestring("$(DDOC)\n"); | |
296 unsigned end = buf2.offset; | |
297 macrotable->expand(&buf2, 0, &end, NULL, 0); | |
298 | |
299 #if 1 | |
300 /* Remove all the escape sequences from buf2, | |
301 * and make CR-LF the newline. | |
302 */ | |
303 { | |
304 buf.setsize(0); | |
305 buf.reserve(buf2.offset); | |
306 unsigned char *p = buf2.data; | |
307 for (unsigned j = 0; j < buf2.offset; j++) | |
308 { | |
309 unsigned char c = p[j]; | |
310 if (c == 0xFF && j + 1 < buf2.offset) | |
311 { | |
312 j++; | |
313 continue; | |
314 } | |
315 if (c == '\n') | |
316 buf.writeByte('\r'); | |
317 else if (c == '\r') | |
318 { | |
319 buf.writestring("\r\n"); | |
320 if (j + 1 < buf2.offset && p[j + 1] == '\n') | |
321 { | |
322 j++; | |
323 } | |
324 continue; | |
325 } | |
326 buf.writeByte(c); | |
327 } | |
328 } | |
329 | |
330 // Transfer image to file | |
331 assert(docfile); | |
332 docfile->setbuffer(buf.data, buf.offset); | |
333 docfile->ref = 1; | |
334 char *pt = FileName::path(docfile->toChars()); | |
335 if (*pt) | |
336 FileName::ensurePathExists(pt); | |
337 mem.free(pt); | |
338 docfile->writev(); | |
339 #else | |
340 /* Remove all the escape sequences from buf2 | |
341 */ | |
342 { unsigned i = 0; | |
343 unsigned char *p = buf2.data; | |
344 for (unsigned j = 0; j < buf2.offset; j++) | |
345 { | |
346 if (p[j] == 0xFF && j + 1 < buf2.offset) | |
347 { | |
348 j++; | |
349 continue; | |
350 } | |
351 p[i] = p[j]; | |
352 i++; | |
353 } | |
354 buf2.setsize(i); | |
355 } | |
356 | |
357 // Transfer image to file | |
358 docfile->setbuffer(buf2.data, buf2.offset); | |
359 docfile->ref = 1; | |
360 char *pt = FileName::path(docfile->toChars()); | |
361 if (*pt) | |
362 FileName::ensurePathExists(pt); | |
363 mem.free(pt); | |
364 docfile->writev(); | |
365 #endif | |
366 } | |
367 | |
368 /******************************* emitComment **********************************/ | |
369 | |
370 /* | |
371 * Emit doc comment to documentation file | |
372 */ | |
373 | |
374 void Dsymbol::emitDitto(Scope *sc) | |
375 { | |
376 OutBuffer *buf = sc->docbuf; | |
377 unsigned o; | |
378 OutBuffer b; | |
379 | |
380 b.writestring("$(DDOC_DITTO "); | |
381 o = b.offset; | |
382 toDocBuffer(&b); | |
383 highlightCode(sc, this, &b, o); | |
384 b.writeByte(')'); | |
385 buf->spread(sc->lastoffset, b.offset); | |
386 memcpy(buf->data + sc->lastoffset, b.data, b.offset); | |
387 sc->lastoffset += b.offset; | |
388 } | |
389 | |
390 void ScopeDsymbol::emitMemberComments(Scope *sc) | |
391 { | |
392 //printf("ScopeDsymbol::emitMemberComments()\n"); | |
393 OutBuffer *buf = sc->docbuf; | |
394 | |
395 if (members) | |
396 { char *m = "$(DDOC_MEMBERS \n"; | |
397 | |
398 if (isModule()) | |
399 m = "$(DDOC_MODULE_MEMBERS \n"; | |
400 else if (isClassDeclaration()) | |
401 m = "$(DDOC_CLASS_MEMBERS \n"; | |
402 else if (isStructDeclaration()) | |
403 m = "$(DDOC_STRUCT_MEMBERS \n"; | |
404 else if (isEnumDeclaration()) | |
405 m = "$(DDOC_ENUM_MEMBERS \n"; | |
406 else if (isTemplateDeclaration()) | |
407 m = "$(DDOC_TEMPLATE_MEMBERS \n"; | |
408 | |
409 // BUG: if no members are actually printed, we should not emit DDOC_MEMBERS | |
410 buf->writestring(m); | |
411 sc = sc->push(this); | |
412 for (int i = 0; i < members->dim; i++) | |
413 { | |
414 Dsymbol *s = (Dsymbol *)members->data[i]; | |
415 //printf("\ts = '%s'\n", s->toChars()); | |
416 s->emitComment(sc); | |
417 } | |
418 sc->pop(); | |
419 buf->writestring(")\n"); | |
420 } | |
421 } | |
422 | |
423 void emitProtection(OutBuffer *buf, PROT prot) | |
424 { | |
425 char *p; | |
426 | |
427 switch (prot) | |
428 { | |
429 case PROTpackage: p = "package"; break; | |
430 case PROTprotected: p = "protected"; break; | |
431 case PROTexport: p = "export"; break; | |
432 default: p = NULL; break; | |
433 } | |
434 if (p) | |
435 buf->printf("%s ", p); | |
436 } | |
437 | |
438 void Dsymbol::emitComment(Scope *sc) { } | |
439 void InvariantDeclaration::emitComment(Scope *sc) { } | |
440 void DtorDeclaration::emitComment(Scope *sc) { } | |
441 void StaticCtorDeclaration::emitComment(Scope *sc) { } | |
442 void StaticDtorDeclaration::emitComment(Scope *sc) { } | |
443 void ClassInfoDeclaration::emitComment(Scope *sc) { } | |
444 void ModuleInfoDeclaration::emitComment(Scope *sc) { } | |
445 void TypeInfoDeclaration::emitComment(Scope *sc) { } | |
446 | |
447 | |
448 void Declaration::emitComment(Scope *sc) | |
449 { | |
450 //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); | |
451 //printf("type = %p\n", type); | |
452 | |
453 if (protection == PROTprivate || !ident || | |
454 (!type && !isCtorDeclaration() && !isAliasDeclaration())) | |
455 return; | |
456 if (!comment) | |
457 return; | |
458 | |
459 OutBuffer *buf = sc->docbuf; | |
460 DocComment *dc = DocComment::parse(sc, this, comment); | |
461 unsigned o; | |
462 | |
463 if (!dc) | |
464 { | |
465 emitDitto(sc); | |
466 return; | |
467 } | |
468 dc->pmacrotable = &sc->module->macrotable; | |
469 | |
470 buf->writestring(ddoc_decl_s); | |
471 o = buf->offset; | |
472 toDocBuffer(buf); | |
473 highlightCode(sc, this, buf, o); | |
474 sc->lastoffset = buf->offset; | |
475 buf->writestring(ddoc_decl_e); | |
476 | |
477 buf->writestring(ddoc_decl_dd_s); | |
478 dc->writeSections(sc, this, buf); | |
479 buf->writestring(ddoc_decl_dd_e); | |
480 } | |
481 | |
482 void AggregateDeclaration::emitComment(Scope *sc) | |
483 { | |
484 //printf("AggregateDeclaration::emitComment() '%s'\n", toChars()); | |
485 if (prot() == PROTprivate) | |
486 return; | |
487 if (!comment) | |
488 return; | |
489 | |
490 OutBuffer *buf = sc->docbuf; | |
491 DocComment *dc = DocComment::parse(sc, this, comment); | |
492 | |
493 if (!dc) | |
494 { | |
495 emitDitto(sc); | |
496 return; | |
497 } | |
498 dc->pmacrotable = &sc->module->macrotable; | |
499 | |
500 buf->writestring(ddoc_decl_s); | |
501 toDocBuffer(buf); | |
502 sc->lastoffset = buf->offset; | |
503 buf->writestring(ddoc_decl_e); | |
504 | |
505 buf->writestring(ddoc_decl_dd_s); | |
506 dc->writeSections(sc, this, buf); | |
507 emitMemberComments(sc); | |
508 buf->writestring(ddoc_decl_dd_e); | |
509 } | |
510 | |
511 void TemplateDeclaration::emitComment(Scope *sc) | |
512 { | |
513 //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind()); | |
514 if (prot() == PROTprivate) | |
515 return; | |
516 if (!comment) | |
517 return; | |
518 | |
519 OutBuffer *buf = sc->docbuf; | |
520 DocComment *dc = DocComment::parse(sc, this, comment); | |
521 unsigned o; | |
522 int hasmembers = 1; | |
523 | |
524 Dsymbol *ss = this; | |
525 | |
526 if (onemember) | |
527 { | |
528 ss = onemember->isAggregateDeclaration(); | |
529 if (!ss) | |
530 { | |
531 ss = onemember->isFuncDeclaration(); | |
532 if (ss) | |
533 hasmembers = 0; | |
534 else | |
535 ss = this; | |
536 } | |
537 } | |
538 | |
539 if (!dc) | |
540 { | |
541 ss->emitDitto(sc); | |
542 return; | |
543 } | |
544 dc->pmacrotable = &sc->module->macrotable; | |
545 | |
546 buf->writestring(ddoc_decl_s); | |
547 o = buf->offset; | |
548 ss->toDocBuffer(buf); | |
549 if (ss == this) | |
550 highlightCode(sc, this, buf, o); | |
551 sc->lastoffset = buf->offset; | |
552 buf->writestring(ddoc_decl_e); | |
553 | |
554 buf->writestring(ddoc_decl_dd_s); | |
555 dc->writeSections(sc, this, buf); | |
556 if (hasmembers) | |
557 ((ScopeDsymbol *)ss)->emitMemberComments(sc); | |
558 buf->writestring(ddoc_decl_dd_e); | |
559 } | |
560 | |
561 void EnumDeclaration::emitComment(Scope *sc) | |
562 { | |
563 if (prot() == PROTprivate) | |
564 return; | |
565 // if (!comment) | |
566 { if (isAnonymous() && members) | |
567 { | |
568 for (int i = 0; i < members->dim; i++) | |
569 { | |
570 Dsymbol *s = (Dsymbol *)members->data[i]; | |
571 s->emitComment(sc); | |
572 } | |
573 return; | |
574 } | |
575 } | |
576 if (!comment) | |
577 return; | |
578 if (isAnonymous()) | |
579 return; | |
580 | |
581 OutBuffer *buf = sc->docbuf; | |
582 DocComment *dc = DocComment::parse(sc, this, comment); | |
583 | |
584 if (!dc) | |
585 { | |
586 emitDitto(sc); | |
587 return; | |
588 } | |
589 dc->pmacrotable = &sc->module->macrotable; | |
590 | |
591 buf->writestring(ddoc_decl_s); | |
592 toDocBuffer(buf); | |
593 sc->lastoffset = buf->offset; | |
594 buf->writestring(ddoc_decl_e); | |
595 | |
596 buf->writestring(ddoc_decl_dd_s); | |
597 dc->writeSections(sc, this, buf); | |
598 emitMemberComments(sc); | |
599 buf->writestring(ddoc_decl_dd_e); | |
600 } | |
601 | |
602 void EnumMember::emitComment(Scope *sc) | |
603 { | |
604 //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); | |
605 if (prot() == PROTprivate) | |
606 return; | |
607 if (!comment) | |
608 return; | |
609 | |
610 OutBuffer *buf = sc->docbuf; | |
611 DocComment *dc = DocComment::parse(sc, this, comment); | |
612 unsigned o; | |
613 | |
614 if (!dc) | |
615 { | |
616 emitDitto(sc); | |
617 return; | |
618 } | |
619 dc->pmacrotable = &sc->module->macrotable; | |
620 | |
621 buf->writestring(ddoc_decl_s); | |
622 o = buf->offset; | |
623 toDocBuffer(buf); | |
624 highlightCode(sc, this, buf, o); | |
625 sc->lastoffset = buf->offset; | |
626 buf->writestring(ddoc_decl_e); | |
627 | |
628 buf->writestring(ddoc_decl_dd_s); | |
629 dc->writeSections(sc, this, buf); | |
630 buf->writestring(ddoc_decl_dd_e); | |
631 } | |
632 | |
633 /******************************* toDocBuffer **********************************/ | |
634 | |
635 void Dsymbol::toDocBuffer(OutBuffer *buf) | |
636 { | |
637 //printf("Dsymbol::toDocbuffer() %s\n", toChars()); | |
638 HdrGenState hgs; | |
639 | |
640 toCBuffer(buf, &hgs); | |
641 } | |
642 | |
643 void prefix(OutBuffer *buf, Dsymbol *s) | |
644 { | |
645 if (s->isDeprecated()) | |
646 buf->writestring("deprecated "); | |
647 Declaration *d = s->isDeclaration(); | |
648 if (d) | |
649 { | |
650 emitProtection(buf, d->protection); | |
651 if (d->isAbstract()) | |
652 buf->writestring("abstract "); | |
653 if (d->isStatic()) | |
654 buf->writestring("static "); | |
655 if (d->isConst()) | |
656 buf->writestring("const "); | |
657 if (d->isFinal()) | |
658 buf->writestring("final "); | |
659 if (d->isSynchronized()) | |
660 buf->writestring("synchronized "); | |
661 } | |
662 } | |
663 | |
664 void Declaration::toDocBuffer(OutBuffer *buf) | |
665 { | |
666 //printf("Declaration::toDocbuffer() %s\n", toChars()); | |
667 if (ident) | |
668 { | |
669 prefix(buf, this); | |
670 | |
671 if (type) | |
672 { HdrGenState hgs; | |
673 hgs.ddoc = 1; | |
674 type->toCBuffer(buf, ident, &hgs); | |
675 } | |
676 else | |
677 buf->writestring(ident->toChars()); | |
678 buf->writestring(";\n"); | |
679 } | |
680 } | |
681 | |
682 | |
683 void AliasDeclaration::toDocBuffer(OutBuffer *buf) | |
684 { | |
685 //printf("AliasDeclaration::toDocbuffer() %s\n", toChars()); | |
686 if (ident) | |
687 { | |
688 if (isDeprecated()) | |
689 buf->writestring("deprecated "); | |
690 | |
691 emitProtection(buf, protection); | |
692 buf->writestring("alias "); | |
693 buf->writestring(toChars()); | |
694 buf->writestring(";\n"); | |
695 } | |
696 } | |
697 | |
698 | |
699 void TypedefDeclaration::toDocBuffer(OutBuffer *buf) | |
700 { | |
701 if (ident) | |
702 { | |
703 if (isDeprecated()) | |
704 buf->writestring("deprecated "); | |
705 | |
706 emitProtection(buf, protection); | |
707 buf->writestring("typedef "); | |
708 buf->writestring(toChars()); | |
709 buf->writestring(";\n"); | |
710 } | |
711 } | |
712 | |
713 | |
714 void FuncDeclaration::toDocBuffer(OutBuffer *buf) | |
715 { | |
716 //printf("FuncDeclaration::toDocbuffer() %s\n", toChars()); | |
717 if (ident) | |
718 { | |
719 TemplateDeclaration *td; | |
720 | |
721 if (parent && | |
722 (td = parent->isTemplateDeclaration()) != NULL && | |
723 td->onemember == this) | |
724 { HdrGenState hgs; | |
725 unsigned o = buf->offset; | |
726 | |
727 hgs.ddoc = 1; | |
728 prefix(buf, td); | |
729 type->next->toCBuffer(buf, NULL, &hgs); | |
730 buf->writeByte(' '); | |
731 buf->writestring(ident->toChars()); | |
732 buf->writeByte('('); | |
733 for (int i = 0; i < td->parameters->dim; i++) | |
734 { | |
735 TemplateParameter *tp = (TemplateParameter *)td->parameters->data[i]; | |
736 if (i) | |
737 buf->writeByte(','); | |
738 tp->toCBuffer(buf, &hgs); | |
739 } | |
740 buf->writeByte(')'); | |
741 TypeFunction *tf = (TypeFunction *)type; | |
742 Argument::argsToCBuffer(buf, &hgs, tf->parameters, tf->varargs); | |
743 buf->writestring(";\n"); | |
744 | |
745 highlightCode(NULL, this, buf, o); | |
746 } | |
747 else | |
748 { | |
749 Declaration::toDocBuffer(buf); | |
750 } | |
751 } | |
752 } | |
753 | |
754 void CtorDeclaration::toDocBuffer(OutBuffer *buf) | |
755 { | |
756 HdrGenState hgs; | |
757 | |
758 buf->writestring("this"); | |
759 Argument::argsToCBuffer(buf, &hgs, arguments, varargs); | |
760 buf->writestring(";\n"); | |
761 } | |
762 | |
763 | |
764 void AggregateDeclaration::toDocBuffer(OutBuffer *buf) | |
765 { | |
766 if (ident) | |
767 { | |
768 #if 0 | |
769 emitProtection(buf, protection); | |
770 #endif | |
771 buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); | |
772 buf->writestring(";\n"); | |
773 } | |
774 } | |
775 | |
776 void StructDeclaration::toDocBuffer(OutBuffer *buf) | |
777 { | |
778 //printf("StructDeclaration::toDocbuffer() %s\n", toChars()); | |
779 if (ident) | |
780 { | |
781 #if 0 | |
782 emitProtection(buf, protection); | |
783 #endif | |
784 TemplateDeclaration *td; | |
785 | |
786 if (parent && | |
787 (td = parent->isTemplateDeclaration()) != NULL && | |
788 td->onemember == this) | |
789 { unsigned o = buf->offset; | |
790 td->toDocBuffer(buf); | |
791 highlightCode(NULL, this, buf, o); | |
792 } | |
793 else | |
794 { | |
795 buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); | |
796 } | |
797 buf->writestring(";\n"); | |
798 } | |
799 } | |
800 | |
801 void ClassDeclaration::toDocBuffer(OutBuffer *buf) | |
802 { | |
803 //printf("ClassDeclaration::toDocbuffer() %s\n", toChars()); | |
804 if (ident) | |
805 { | |
806 #if 0 | |
807 emitProtection(buf, protection); | |
808 #endif | |
809 TemplateDeclaration *td; | |
810 | |
811 if (parent && | |
812 (td = parent->isTemplateDeclaration()) != NULL && | |
813 td->onemember == this) | |
814 { unsigned o = buf->offset; | |
815 td->toDocBuffer(buf); | |
816 highlightCode(NULL, this, buf, o); | |
817 } | |
818 else | |
819 { | |
820 buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); | |
821 } | |
822 int any = 0; | |
823 for (int i = 0; i < baseclasses.dim; i++) | |
824 { BaseClass *bc = (BaseClass *)baseclasses.data[i]; | |
825 | |
826 if (bc->protection == PROTprivate) | |
827 continue; | |
828 if (bc->base && bc->base->ident == Id::Object) | |
829 continue; | |
830 | |
831 if (any) | |
832 buf->writestring(", "); | |
833 else | |
834 { buf->writestring(": "); | |
835 any = 1; | |
836 } | |
837 emitProtection(buf, bc->protection); | |
838 if (bc->base) | |
839 { | |
840 buf->writestring(bc->base->toPrettyChars()); | |
841 } | |
842 else | |
843 { | |
844 HdrGenState hgs; | |
845 bc->type->toCBuffer(buf, NULL, &hgs); | |
846 } | |
847 } | |
848 buf->writestring(";\n"); | |
849 } | |
850 } | |
851 | |
852 | |
853 void EnumDeclaration::toDocBuffer(OutBuffer *buf) | |
854 { | |
855 if (ident) | |
856 { | |
857 buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); | |
858 buf->writestring(";\n"); | |
859 } | |
860 } | |
861 | |
862 void EnumMember::toDocBuffer(OutBuffer *buf) | |
863 { | |
864 if (ident) | |
865 { | |
866 buf->writestring(toChars()); | |
867 } | |
868 } | |
869 | |
870 | |
871 /********************************* DocComment *********************************/ | |
872 | |
873 DocComment::DocComment() | |
874 { | |
875 memset(this, 0, sizeof(DocComment)); | |
876 } | |
877 | |
878 DocComment *DocComment::parse(Scope *sc, Dsymbol *s, unsigned char *comment) | |
879 { unsigned idlen; | |
880 | |
881 if (sc->lastdc && isDitto(comment)) | |
882 return NULL; | |
883 | |
884 DocComment *dc = new DocComment(); | |
885 if (!comment) | |
886 return dc; | |
887 | |
888 dc->parseSections(comment); | |
889 | |
890 for (int i = 0; i < dc->sections.dim; i++) | |
891 { Section *s = (Section *)dc->sections.data[i]; | |
892 | |
893 if (icmp("copyright", s->name, s->namelen) == 0) | |
894 { | |
895 dc->copyright = s; | |
896 } | |
897 if (icmp("macros", s->name, s->namelen) == 0) | |
898 { | |
899 dc->macros = s; | |
900 } | |
901 } | |
902 | |
903 sc->lastdc = dc; | |
904 return dc; | |
905 } | |
906 | |
907 /***************************************** | |
908 * Parse next paragraph out of *pcomment. | |
909 * Update *pcomment to point past paragraph. | |
910 * Returns NULL if no more paragraphs. | |
911 * If paragraph ends in 'identifier:', | |
912 * then (*pcomment)[0 .. idlen] is the identifier. | |
913 */ | |
914 | |
915 void DocComment::parseSections(unsigned char *comment) | |
916 { unsigned char *p; | |
917 unsigned char *pstart; | |
918 unsigned char *pend; | |
919 unsigned char *q; | |
920 unsigned char *idstart; | |
921 unsigned idlen; | |
922 | |
923 unsigned char *name = NULL; | |
924 unsigned namelen = 0; | |
925 | |
926 p = comment; | |
927 while (*p) | |
928 { | |
929 p = skipwhitespace(p); | |
930 pstart = p; | |
931 | |
932 /* Find end of section, which is ended by one of: | |
933 * 'identifier:' | |
934 * '\0' | |
935 */ | |
936 idlen = 0; | |
937 while (1) | |
938 { | |
939 if (isalpha(*p) || *p == '_') | |
940 { | |
941 q = p + 1; | |
942 while (isalnum(*q) || *q == '_') | |
943 q++; | |
944 if (*q == ':') // identifier: ends it | |
945 { idlen = q - p; | |
946 idstart = p; | |
947 for (pend = p; pend > pstart; pend--) | |
948 { if (pend[-1] == '\n') | |
949 break; | |
950 } | |
951 p = q + 1; | |
952 break; | |
953 } | |
954 } | |
955 while (1) | |
956 { | |
957 if (!*p) | |
958 { pend = p; | |
959 goto L1; | |
960 } | |
961 if (*p == '\n') | |
962 { p++; | |
963 if (*p == '\n' && !summary && !namelen) | |
964 { | |
965 pend = p; | |
966 p++; | |
967 goto L1; | |
968 } | |
969 break; | |
970 } | |
971 p++; | |
972 } | |
973 p = skipwhitespace(p); | |
974 } | |
975 L1: | |
976 | |
977 if (namelen || pstart < pend) | |
978 { | |
979 Section *s; | |
980 if (icmp("Params", name, namelen) == 0) | |
981 s = new ParamSection(); | |
982 else if (icmp("Macros", name, namelen) == 0) | |
983 s = new MacroSection(); | |
984 else | |
985 s = new Section(); | |
986 s->name = name; | |
987 s->namelen = namelen; | |
988 s->body = pstart; | |
989 s->bodylen = pend - pstart; | |
990 s->nooutput = 0; | |
991 | |
992 //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body); | |
993 | |
994 sections.push(s); | |
995 | |
996 if (!summary && !namelen) | |
997 summary = s; | |
998 } | |
999 | |
1000 if (idlen) | |
1001 { name = idstart; | |
1002 namelen = idlen; | |
1003 } | |
1004 else | |
1005 { name = NULL; | |
1006 namelen = 0; | |
1007 if (!*p) | |
1008 break; | |
1009 } | |
1010 } | |
1011 } | |
1012 | |
1013 void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf) | |
1014 { | |
1015 //printf("DocComment::writeSections()\n"); | |
1016 if (sections.dim) | |
1017 { | |
1018 buf->writestring("$(DDOC_SECTIONS \n"); | |
1019 for (int i = 0; i < sections.dim; i++) | |
1020 { Section *sec = (Section *)sections.data[i]; | |
1021 | |
1022 if (sec->nooutput) | |
1023 continue; | |
1024 //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body); | |
1025 if (sec->namelen || i) | |
1026 sec->write(this, sc, s, buf); | |
1027 else | |
1028 { | |
1029 buf->writestring("$(DDOC_SUMMARY "); | |
1030 unsigned o = buf->offset; | |
1031 buf->write(sec->body, sec->bodylen); | |
1032 highlightText(sc, s, buf, o); | |
1033 buf->writestring(")\n"); | |
1034 } | |
1035 } | |
1036 buf->writestring(")\n"); | |
1037 } | |
1038 else | |
1039 { | |
1040 buf->writestring("$(DDOC_BLANKLINE)\n"); | |
1041 } | |
1042 } | |
1043 | |
1044 /*************************************************** | |
1045 */ | |
1046 | |
1047 void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) | |
1048 { | |
1049 if (namelen) | |
1050 { | |
1051 static char *table[] = | |
1052 { "AUTHORS", "BUGS", "COPYRIGHT", "DATE", | |
1053 "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE", | |
1054 "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS", | |
1055 "VERSION" }; | |
1056 | |
1057 for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) | |
1058 { | |
1059 if (icmp(table[i], name, namelen) == 0) | |
1060 { | |
1061 buf->printf("$(DDOC_%s ", table[i]); | |
1062 goto L1; | |
1063 } | |
1064 } | |
1065 | |
1066 buf->writestring("$(DDOC_SECTION "); | |
1067 // Replace _ characters with spaces | |
1068 buf->writestring("$(DDOC_SECTION_H "); | |
1069 for (unsigned u = 0; u < namelen; u++) | |
1070 { unsigned char c = name[u]; | |
1071 buf->writeByte((c == '_') ? ' ' : c); | |
1072 } | |
1073 buf->writestring(":)\n"); | |
1074 } | |
1075 else | |
1076 { | |
1077 buf->writestring("$(DDOC_DESCRIPTION "); | |
1078 } | |
1079 L1: | |
1080 unsigned o = buf->offset; | |
1081 buf->write(body, bodylen); | |
1082 highlightText(sc, s, buf, o); | |
1083 buf->writestring(")\n"); | |
1084 } | |
1085 | |
1086 /*************************************************** | |
1087 */ | |
1088 | |
1089 void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) | |
1090 { | |
1091 unsigned char *p = body; | |
1092 unsigned len = bodylen; | |
1093 unsigned char *pend = p + len; | |
1094 | |
1095 unsigned char *tempstart; | |
1096 unsigned templen; | |
1097 | |
1098 unsigned char *namestart; | |
1099 unsigned namelen = 0; // !=0 if line continuation | |
1100 | |
1101 unsigned char *textstart; | |
1102 unsigned textlen; | |
1103 | |
1104 unsigned o; | |
1105 Argument *arg; | |
1106 | |
1107 buf->writestring("$(DDOC_PARAMS \n"); | |
1108 while (p < pend) | |
1109 { | |
1110 // Skip to start of macro | |
1111 for (; 1; p++) | |
1112 { | |
1113 switch (*p) | |
1114 { | |
1115 case ' ': | |
1116 case '\t': | |
1117 continue; | |
1118 | |
1119 case '\n': | |
1120 p++; | |
1121 goto Lcont; | |
1122 | |
1123 default: | |
1124 if (!(isalpha(*p) || *p == '_')) | |
1125 { | |
1126 if (namelen) | |
1127 goto Ltext; // continuation of prev macro | |
1128 goto Lskipline; | |
1129 } | |
1130 break; | |
1131 } | |
1132 break; | |
1133 } | |
1134 tempstart = p; | |
1135 | |
1136 while (isalnum(*p) || *p == '_') | |
1137 p++; | |
1138 templen = p - tempstart; | |
1139 | |
1140 while (*p == ' ' || *p == '\t') | |
1141 p++; | |
1142 | |
1143 if (*p != '=') | |
1144 { if (namelen) | |
1145 goto Ltext; // continuation of prev macro | |
1146 goto Lskipline; | |
1147 } | |
1148 p++; | |
1149 | |
1150 if (namelen) | |
1151 { // Output existing param | |
1152 | |
1153 L1: | |
1154 //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); | |
1155 HdrGenState hgs; | |
1156 buf->writestring("$(DDOC_PARAM_ROW "); | |
1157 buf->writestring("$(DDOC_PARAM_ID "); | |
1158 o = buf->offset; | |
1159 arg = isFunctionParameter(s, namestart, namelen); | |
1160 if (arg && arg->type && arg->ident) | |
1161 arg->type->toCBuffer(buf, arg->ident, &hgs); | |
1162 else | |
1163 buf->write(namestart, namelen); | |
1164 highlightCode(sc, s, buf, o); | |
1165 buf->writestring(")\n"); | |
1166 | |
1167 buf->writestring("$(DDOC_PARAM_DESC "); | |
1168 o = buf->offset; | |
1169 buf->write(textstart, textlen); | |
1170 highlightText(sc, s, buf, o); | |
1171 buf->writestring(")"); | |
1172 buf->writestring(")\n"); | |
1173 namelen = 0; | |
1174 if (p >= pend) | |
1175 break; | |
1176 } | |
1177 | |
1178 namestart = tempstart; | |
1179 namelen = templen; | |
1180 | |
1181 while (*p == ' ' || *p == '\t') | |
1182 p++; | |
1183 textstart = p; | |
1184 | |
1185 Ltext: | |
1186 while (*p != '\n') | |
1187 p++; | |
1188 textlen = p - textstart; | |
1189 p++; | |
1190 | |
1191 Lcont: | |
1192 continue; | |
1193 | |
1194 Lskipline: | |
1195 // Ignore this line | |
1196 while (*p++ != '\n') | |
1197 ; | |
1198 } | |
1199 if (namelen) | |
1200 goto L1; // write out last one | |
1201 buf->writestring(")\n"); | |
1202 } | |
1203 | |
1204 /*************************************************** | |
1205 */ | |
1206 | |
1207 void MacroSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) | |
1208 { | |
1209 //printf("MacroSection::write()\n"); | |
1210 DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen); | |
1211 } | |
1212 | |
1213 /************************************************ | |
1214 * Parse macros out of Macros: section. | |
1215 * Macros are of the form: | |
1216 * name1 = value1 | |
1217 * | |
1218 * name2 = value2 | |
1219 */ | |
1220 | |
1221 void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen) | |
1222 { | |
1223 unsigned char *p = m; | |
1224 unsigned len = mlen; | |
1225 unsigned char *pend = p + len; | |
1226 | |
1227 unsigned char *tempstart; | |
1228 unsigned templen; | |
1229 | |
1230 unsigned char *namestart; | |
1231 unsigned namelen = 0; // !=0 if line continuation | |
1232 | |
1233 unsigned char *textstart; | |
1234 unsigned textlen; | |
1235 | |
1236 while (p < pend) | |
1237 { | |
1238 // Skip to start of macro | |
1239 for (; 1; p++) | |
1240 { | |
1241 if (p >= pend) | |
1242 goto Ldone; | |
1243 switch (*p) | |
1244 { | |
1245 case ' ': | |
1246 case '\t': | |
1247 continue; | |
1248 | |
1249 case '\n': | |
1250 p++; | |
1251 goto Lcont; | |
1252 | |
1253 default: | |
1254 if (!(isalpha(*p) || *p == '_')) | |
1255 { | |
1256 if (namelen) | |
1257 goto Ltext; // continuation of prev macro | |
1258 goto Lskipline; | |
1259 } | |
1260 break; | |
1261 } | |
1262 break; | |
1263 } | |
1264 tempstart = p; | |
1265 | |
1266 while (1) | |
1267 { | |
1268 if (p >= pend) | |
1269 goto Ldone; | |
1270 if (!(isalnum(*p) || *p == '_')) | |
1271 break; | |
1272 p++; | |
1273 } | |
1274 templen = p - tempstart; | |
1275 | |
1276 while (1) | |
1277 { | |
1278 if (p >= pend) | |
1279 goto Ldone; | |
1280 if (!(*p == ' ' || *p == '\t')) | |
1281 break; | |
1282 p++; | |
1283 } | |
1284 | |
1285 if (*p != '=') | |
1286 { if (namelen) | |
1287 goto Ltext; // continuation of prev macro | |
1288 goto Lskipline; | |
1289 } | |
1290 p++; | |
1291 if (p >= pend) | |
1292 goto Ldone; | |
1293 | |
1294 if (namelen) | |
1295 { // Output existing macro | |
1296 L1: | |
1297 //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart); | |
1298 if (icmp("ESCAPES", namestart, namelen) == 0) | |
1299 parseEscapes(pescapetable, textstart, textlen); | |
1300 else | |
1301 Macro::define(pmacrotable, namestart, namelen, textstart, textlen); | |
1302 namelen = 0; | |
1303 if (p >= pend) | |
1304 break; | |
1305 } | |
1306 | |
1307 namestart = tempstart; | |
1308 namelen = templen; | |
1309 | |
1310 while (p < pend && (*p == ' ' || *p == '\t')) | |
1311 p++; | |
1312 textstart = p; | |
1313 | |
1314 Ltext: | |
1315 while (p < pend && *p != '\n') | |
1316 p++; | |
1317 textlen = p - textstart; | |
1318 | |
1319 // Remove trailing \r if there is one | |
1320 if (p > m && p[-1] == '\r') | |
1321 textlen--; | |
1322 | |
1323 p++; | |
1324 //printf("p = %p, pend = %p\n", p, pend); | |
1325 | |
1326 Lcont: | |
1327 continue; | |
1328 | |
1329 Lskipline: | |
1330 // Ignore this line | |
1331 while (p < pend && *p++ != '\n') | |
1332 ; | |
1333 } | |
1334 Ldone: | |
1335 if (namelen) | |
1336 goto L1; // write out last one | |
1337 } | |
1338 | |
1339 /************************************** | |
1340 * Parse escapes of the form: | |
1341 * /c/string/ | |
1342 * where c is a single character. | |
1343 * Multiple escapes can be separated | |
1344 * by whitespace and/or commas. | |
1345 */ | |
1346 | |
1347 void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen) | |
1348 { Escape *escapetable = *pescapetable; | |
1349 | |
1350 if (!escapetable) | |
1351 { escapetable = new Escape; | |
1352 *pescapetable = escapetable; | |
1353 } | |
1354 unsigned char *p = textstart; | |
1355 unsigned char *pend = p + textlen; | |
1356 | |
1357 while (1) | |
1358 { | |
1359 while (1) | |
1360 { | |
1361 if (p + 4 >= pend) | |
1362 return; | |
1363 if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ',')) | |
1364 break; | |
1365 p++; | |
1366 } | |
1367 if (p[0] != '/' || p[2] != '/') | |
1368 return; | |
1369 unsigned char c = p[1]; | |
1370 p += 3; | |
1371 unsigned char *start = p; | |
1372 while (1) | |
1373 { | |
1374 if (p >= pend) | |
1375 return; | |
1376 if (*p == '/') | |
1377 break; | |
1378 p++; | |
1379 } | |
1380 size_t len = p - start; | |
1381 char *s = (char *)memcpy(mem.malloc(len + 1), start, len); | |
1382 s[len] = 0; | |
1383 escapetable->strings[c] = s; | |
1384 //printf("%c = '%s'\n", c, s); | |
1385 p++; | |
1386 } | |
1387 } | |
1388 | |
1389 | |
1390 /****************************************** | |
1391 * Compare 0-terminated string with length terminated string. | |
1392 * Return < 0, ==0, > 0 | |
1393 */ | |
1394 | |
1395 int cmp(char *stringz, void *s, size_t slen) | |
1396 { | |
1397 size_t len1 = strlen(stringz); | |
1398 | |
1399 if (len1 != slen) | |
1400 return len1 - slen; | |
1401 return memcmp(stringz, s, slen); | |
1402 } | |
1403 | |
1404 int icmp(char *stringz, void *s, size_t slen) | |
1405 { | |
1406 size_t len1 = strlen(stringz); | |
1407 | |
1408 if (len1 != slen) | |
1409 return len1 - slen; | |
1410 return memicmp(stringz, (char *)s, slen); | |
1411 } | |
1412 | |
1413 /***************************************** | |
1414 * Return !=0 if comment consists entirely of "ditto". | |
1415 */ | |
1416 | |
1417 int isDitto(unsigned char *comment) | |
1418 { | |
1419 if (comment) | |
1420 { | |
1421 unsigned char *p = skipwhitespace(comment); | |
1422 | |
1423 if (memicmp((char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0) | |
1424 return 1; | |
1425 } | |
1426 return 0; | |
1427 } | |
1428 | |
1429 /********************************************** | |
1430 * Skip white space. | |
1431 */ | |
1432 | |
1433 unsigned char *skipwhitespace(unsigned char *p) | |
1434 { | |
1435 for (; 1; p++) | |
1436 { switch (*p) | |
1437 { | |
1438 case ' ': | |
1439 case '\t': | |
1440 case '\n': | |
1441 continue; | |
1442 } | |
1443 break; | |
1444 } | |
1445 return p; | |
1446 } | |
1447 | |
1448 | |
1449 /************************************************ | |
1450 * Scan forward to one of: | |
1451 * start of identifier | |
1452 * beginning of next line | |
1453 * end of buf | |
1454 */ | |
1455 | |
1456 unsigned skiptoident(OutBuffer *buf, unsigned i) | |
1457 { | |
1458 for (; i < buf->offset; i++) | |
1459 { | |
1460 // BUG: handle unicode alpha's | |
1461 unsigned char c = buf->data[i]; | |
1462 if (isalpha(c) || c == '_') | |
1463 break; | |
1464 if (c == '\n') | |
1465 break; | |
1466 } | |
1467 return i; | |
1468 } | |
1469 | |
1470 /************************************************ | |
1471 * Scan forward past end of identifier. | |
1472 */ | |
1473 | |
1474 unsigned skippastident(OutBuffer *buf, unsigned i) | |
1475 { | |
1476 for (; i < buf->offset; i++) | |
1477 { | |
1478 // BUG: handle unicode alpha's | |
1479 unsigned char c = buf->data[i]; | |
1480 if (!(isalnum(c) || c == '_')) | |
1481 break; | |
1482 } | |
1483 return i; | |
1484 } | |
1485 | |
1486 | |
1487 /**************************************************** | |
1488 */ | |
1489 | |
1490 int isKeyword(unsigned char *p, unsigned len) | |
1491 { | |
1492 static char *table[] = { "true", "false", "null" }; | |
1493 | |
1494 for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) | |
1495 { | |
1496 if (cmp(table[i], p, len) == 0) | |
1497 return 1; | |
1498 } | |
1499 return 0; | |
1500 } | |
1501 | |
1502 /**************************************************** | |
1503 */ | |
1504 | |
1505 Argument *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len) | |
1506 { | |
1507 FuncDeclaration *f = s->isFuncDeclaration(); | |
1508 | |
1509 /* f->type may be NULL for template members. | |
1510 */ | |
1511 if (f && f->type) | |
1512 { | |
1513 TypeFunction *tf = (TypeFunction *)f->type; | |
1514 | |
1515 if (tf->parameters) | |
1516 { | |
1517 for (size_t k = 0; k < tf->parameters->dim; k++) | |
1518 { Argument *arg = (Argument *)tf->parameters->data[k]; | |
1519 | |
1520 if (arg->ident && cmp(arg->ident->toChars(), p, len) == 0) | |
1521 { | |
1522 return arg; | |
1523 } | |
1524 } | |
1525 } | |
1526 } | |
1527 return NULL; | |
1528 } | |
1529 | |
1530 /************************************************** | |
1531 * Highlight text section. | |
1532 */ | |
1533 | |
1534 void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) | |
1535 { | |
1536 //printf("highlightText()\n"); | |
1537 char *sid = s->ident->toChars(); | |
1538 FuncDeclaration *f = s->isFuncDeclaration(); | |
1539 unsigned char *p; | |
1540 char *se; | |
1541 | |
1542 int leadingBlank = 1; | |
1543 int inCode = 0; | |
1544 int inComment = 0; // in <!-- ... --> comment | |
1545 unsigned iCodeStart; // start of code section | |
1546 | |
1547 unsigned iLineStart = offset; | |
1548 | |
1549 for (unsigned i = offset; i < buf->offset; i++) | |
1550 { unsigned char c = buf->data[i]; | |
1551 | |
1552 Lcont: | |
1553 switch (c) | |
1554 { | |
1555 case ' ': | |
1556 case '\t': | |
1557 break; | |
1558 | |
1559 case '\n': | |
1560 if (sc && !inCode && i == iLineStart && i + 1 < buf->offset) // if "\n\n" | |
1561 { | |
1562 static char blankline[] = "$(DDOC_BLANKLINE)\n"; | |
1563 | |
1564 i = buf->insert(i, blankline, sizeof(blankline) - 1); | |
1565 } | |
1566 leadingBlank = 1; | |
1567 iLineStart = i + 1; | |
1568 break; | |
1569 | |
1570 case '<': | |
1571 leadingBlank = 0; | |
1572 if (inCode) | |
1573 break; | |
1574 p = &buf->data[i]; | |
1575 | |
1576 // Skip over comments | |
1577 if (p[1] == '!' && p[2] == '-' && p[3] == '-') | |
1578 { unsigned j = i + 4; | |
1579 p += 4; | |
1580 while (1) | |
1581 { | |
1582 if (j == buf->offset) | |
1583 goto L1; | |
1584 if (p[0] == '-' && p[1] == '-' && p[2] == '>') | |
1585 { | |
1586 i = j + 2; // place on closing '>' | |
1587 break; | |
1588 } | |
1589 j++; | |
1590 p++; | |
1591 } | |
1592 break; | |
1593 } | |
1594 | |
1595 // Skip over HTML tag | |
1596 if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2]))) | |
1597 { unsigned j = i + 2; | |
1598 p += 2; | |
1599 while (1) | |
1600 { | |
1601 if (j == buf->offset) | |
1602 goto L1; | |
1603 if (p[0] == '>') | |
1604 { | |
1605 i = j; // place on closing '>' | |
1606 break; | |
1607 } | |
1608 j++; | |
1609 p++; | |
1610 } | |
1611 break; | |
1612 } | |
1613 | |
1614 L1: | |
1615 // Replace '<' with '<' character entity | |
1616 se = Escape::escapeChar('<'); | |
1617 if (se) | |
1618 { size_t len = strlen(se); | |
1619 buf->remove(i, 1); | |
1620 i = buf->insert(i, se, len); | |
1621 i--; // point to ';' | |
1622 } | |
1623 break; | |
1624 | |
1625 case '>': | |
1626 leadingBlank = 0; | |
1627 if (inCode) | |
1628 break; | |
1629 // Replace '>' with '>' character entity | |
1630 se = Escape::escapeChar('>'); | |
1631 if (se) | |
1632 { size_t len = strlen(se); | |
1633 buf->remove(i, 1); | |
1634 i = buf->insert(i, se, len); | |
1635 i--; // point to ';' | |
1636 } | |
1637 break; | |
1638 | |
1639 case '&': | |
1640 leadingBlank = 0; | |
1641 if (inCode) | |
1642 break; | |
1643 p = &buf->data[i]; | |
1644 if (p[1] == '#' || isalpha(p[1])) | |
1645 break; // already a character entity | |
1646 // Replace '&' with '&' character entity | |
1647 se = Escape::escapeChar('&'); | |
1648 if (se) | |
1649 { size_t len = strlen(se); | |
1650 buf->remove(i, 1); | |
1651 i = buf->insert(i, se, len); | |
1652 i--; // point to ';' | |
1653 } | |
1654 break; | |
1655 | |
1656 case '-': | |
1657 /* A line beginning with --- delimits a code section. | |
1658 * inCode tells us if it is start or end of a code section. | |
1659 */ | |
1660 if (leadingBlank) | |
1661 { int istart = i; | |
1662 int eollen = 0; | |
1663 | |
1664 leadingBlank = 0; | |
1665 while (1) | |
1666 { | |
1667 ++i; | |
1668 if (i >= buf->offset) | |
1669 break; | |
1670 c = buf->data[i]; | |
1671 if (c == '\n') | |
1672 { eollen = 1; | |
1673 break; | |
1674 } | |
1675 if (c == '\r') | |
1676 { | |
1677 eollen = 1; | |
1678 if (i + 1 >= buf->offset) | |
1679 break; | |
1680 if (buf->data[i + 1] == '\n') | |
1681 { eollen = 2; | |
1682 break; | |
1683 } | |
1684 } | |
1685 // BUG: handle UTF PS and LS too | |
1686 if (c != '-') | |
1687 goto Lcont; | |
1688 } | |
1689 if (i - istart < 3) | |
1690 goto Lcont; | |
1691 | |
1692 // We have the start/end of a code section | |
1693 | |
1694 // Remove the entire --- line, including blanks and \n | |
1695 buf->remove(iLineStart, i - iLineStart + eollen); | |
1696 i = iLineStart; | |
1697 | |
1698 if (inCode) | |
1699 { | |
1700 inCode = 0; | |
1701 // The code section is from iCodeStart to i | |
1702 OutBuffer codebuf; | |
1703 | |
1704 codebuf.write(buf->data + iCodeStart, i - iCodeStart); | |
1705 codebuf.writeByte(0); | |
1706 highlightCode2(sc, s, &codebuf, 0); | |
1707 buf->remove(iCodeStart, i - iCodeStart); | |
1708 i = buf->insert(iCodeStart, codebuf.data, codebuf.offset); | |
1709 i = buf->insert(i, ")\n", 2); | |
1710 i--; | |
1711 } | |
1712 else | |
1713 { static char pre[] = "$(D_CODE \n"; | |
1714 | |
1715 inCode = 1; | |
1716 i = buf->insert(i, pre, sizeof(pre) - 1); | |
1717 iCodeStart = i; | |
1718 i--; // place i on > | |
1719 } | |
1720 } | |
1721 break; | |
1722 | |
1723 default: | |
1724 leadingBlank = 0; | |
1725 if (sc && !inCode && (isalpha(c) || c == '_')) | |
1726 { unsigned j; | |
1727 | |
1728 j = skippastident(buf, i); | |
1729 if (j > i) | |
1730 { | |
1731 if (buf->data[i] == '_') // leading '_' means no highlight | |
1732 { | |
1733 buf->remove(i, 1); | |
1734 i = j - 1; | |
1735 } | |
1736 else | |
1737 { | |
1738 if (cmp(sid, buf->data + i, j - i) == 0) | |
1739 { | |
1740 i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; | |
1741 break; | |
1742 } | |
1743 else if (isKeyword(buf->data + i, j - i)) | |
1744 { | |
1745 i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1; | |
1746 break; | |
1747 } | |
1748 else | |
1749 { | |
1750 if (f && isFunctionParameter(f, buf->data + i, j - i)) | |
1751 { | |
1752 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); | |
1753 i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; | |
1754 break; | |
1755 } | |
1756 } | |
1757 i = j - 1; | |
1758 } | |
1759 } | |
1760 } | |
1761 break; | |
1762 } | |
1763 } | |
1764 Ldone: | |
1765 ; | |
1766 } | |
1767 | |
1768 /************************************************** | |
1769 * Highlight code for DDOC section. | |
1770 */ | |
1771 | |
1772 void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) | |
1773 { | |
1774 char *sid = s->ident->toChars(); | |
1775 FuncDeclaration *f = s->isFuncDeclaration(); | |
1776 | |
1777 //printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind()); | |
1778 for (unsigned i = offset; i < buf->offset; i++) | |
1779 { unsigned char c = buf->data[i]; | |
1780 char *se; | |
1781 | |
1782 se = Escape::escapeChar(c); | |
1783 if (se) | |
1784 { | |
1785 size_t len = strlen(se); | |
1786 buf->remove(i, 1); | |
1787 i = buf->insert(i, se, len); | |
1788 i--; // point to ';' | |
1789 } | |
1790 else if (isalpha(c) || c == '_') | |
1791 { unsigned j; | |
1792 | |
1793 j = skippastident(buf, i); | |
1794 if (j > i) | |
1795 { | |
1796 if (cmp(sid, buf->data + i, j - i) == 0) | |
1797 { | |
1798 i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1; | |
1799 continue; | |
1800 } | |
1801 else if (f) | |
1802 { | |
1803 if (isFunctionParameter(f, buf->data + i, j - i)) | |
1804 { | |
1805 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); | |
1806 i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; | |
1807 continue; | |
1808 } | |
1809 } | |
1810 i = j - 1; | |
1811 } | |
1812 } | |
1813 } | |
1814 } | |
1815 | |
1816 /**************************************** | |
1817 */ | |
1818 | |
1819 void highlightCode3(OutBuffer *buf, unsigned char *p, unsigned char *pend) | |
1820 { | |
1821 for (; p < pend; p++) | |
1822 { char *s = Escape::escapeChar(*p); | |
1823 if (s) | |
1824 buf->writestring(s); | |
1825 else | |
1826 buf->writeByte(*p); | |
1827 } | |
1828 } | |
1829 | |
1830 /************************************************** | |
1831 * Highlight code for CODE section. | |
1832 */ | |
1833 | |
1834 | |
1835 void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) | |
1836 { | |
1837 char *sid = s->ident->toChars(); | |
1838 FuncDeclaration *f = s->isFuncDeclaration(); | |
1839 unsigned errorsave = global.errors; | |
1840 Lexer lex(NULL, buf->data, 0, buf->offset - 1, 0, 1); | |
1841 Token tok; | |
1842 OutBuffer res; | |
1843 unsigned char *lastp = buf->data; | |
1844 char *highlight; | |
1845 | |
1846 //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data); | |
1847 res.reserve(buf->offset); | |
1848 while (1) | |
1849 { | |
1850 lex.scan(&tok); | |
1851 highlightCode3(&res, lastp, tok.ptr); | |
1852 highlight = NULL; | |
1853 switch (tok.value) | |
1854 { | |
1855 case TOKidentifier: | |
1856 if (!sc) | |
1857 break; | |
1858 if (cmp(sid, tok.ptr, lex.p - tok.ptr) == 0) | |
1859 { | |
1860 highlight = "$(D_PSYMBOL "; | |
1861 break; | |
1862 } | |
1863 else if (f) | |
1864 { | |
1865 if (isFunctionParameter(f, tok.ptr, lex.p - tok.ptr)) | |
1866 { | |
1867 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); | |
1868 highlight = "$(D_PARAM "; | |
1869 break; | |
1870 } | |
1871 } | |
1872 break; | |
1873 | |
1874 case TOKcomment: | |
1875 highlight = "$(D_COMMENT "; | |
1876 break; | |
1877 | |
1878 case TOKstring: | |
1879 highlight = "$(D_STRING "; | |
1880 break; | |
1881 | |
1882 default: | |
1883 if (tok.isKeyword()) | |
1884 highlight = "$(D_KEYWORD "; | |
1885 break; | |
1886 } | |
1887 if (highlight) | |
1888 res.writestring(highlight); | |
1889 highlightCode3(&res, tok.ptr, lex.p); | |
1890 if (highlight) | |
1891 res.writeByte(')'); | |
1892 if (tok.value == TOKeof) | |
1893 break; | |
1894 lastp = lex.p; | |
1895 } | |
1896 buf->setsize(offset); | |
1897 buf->write(&res); | |
1898 global.errors = errorsave; | |
1899 } | |
1900 | |
1901 /*************************************** | |
1902 * Find character string to replace c with. | |
1903 */ | |
1904 | |
1905 char *Escape::escapeChar(unsigned c) | |
1906 { char *s; | |
1907 | |
1908 switch (c) | |
1909 { | |
1910 case '<': | |
1911 s = "<"; | |
1912 break; | |
1913 case '>': | |
1914 s = ">"; | |
1915 break; | |
1916 case '&': | |
1917 s = "&"; | |
1918 break; | |
1919 default: | |
1920 s = NULL; | |
1921 break; | |
1922 } | |
1923 return s; | |
1924 } | |
1925 |