2 // Compiler implementation of the D programming language
3 // Copyright (c) 1999-2008 by Digital Mars
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.
11 // This implements the Ddoc capability.
23 #include "..\root\mem.h"
25 #include "../root/mem.h"
38 #include "aggregate.h"
39 #include "declaration.h"
52 static char *escapeChar(unsigned c
);
65 virtual void write(DocComment
*dc
, Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
);
68 struct ParamSection
: Section
70 void write(DocComment
*dc
, Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
);
73 struct MacroSection
: Section
75 void write(DocComment
*dc
, Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
);
80 Array sections
; // Section*[]
86 Escape
**pescapetable
;
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
);
94 void parseSections(unsigned char *comment
);
95 void writeSections(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
);
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 unsigned skippastURL(OutBuffer
*buf
, unsigned i
);
106 void highlightText(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, unsigned offset
);
107 void highlightCode(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, unsigned offset
);
108 void highlightCode2(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, unsigned offset
);
109 Argument
*isFunctionParameter(Dsymbol
*s
, unsigned char *p
, unsigned len
);
111 static unsigned char ddoc_default
[] = "\
112 DDOC = <html><head>\n\
113 <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
114 <title>$(TITLE)</title>\n\
118 <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT))\n\
128 TABLE = <table>$0</table>\n\
135 BIG = <big>$0</big>\n\
136 SMALL = <small>$0</small>\n\
138 LINK = <a href=\"$0\">$0</a>\n\
139 LINK2 = <a href=\"$1\">$+</a>\n\
141 RED = <font color=red>$0</font>\n\
142 BLUE = <font color=blue>$0</font>\n\
143 GREEN = <font color=green>$0</font>\n\
144 YELLOW =<font color=yellow>$0</font>\n\
145 BLACK = <font color=black>$0</font>\n\
146 WHITE = <font color=white>$0</font>\n\
148 D_CODE = <pre class=\"d_code\">$0</pre>\n\
149 D_COMMENT = $(GREEN $0)\n\
150 D_STRING = $(RED $0)\n\
151 D_KEYWORD = $(BLUE $0)\n\
152 D_PSYMBOL = $(U $0)\n\
155 DDOC_COMMENT = <!-- $0 -->\n\
156 DDOC_DECL = $(DT $(BIG $0))\n\
157 DDOC_DECL_DD = $(DD $0)\n\
158 DDOC_DITTO = $(BR)$0\n\
159 DDOC_SECTIONS = $0\n\
160 DDOC_SUMMARY = $0$(BR)$(BR)\n\
161 DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
162 DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
163 DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
164 DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
165 DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
166 DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
167 DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
168 DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
169 DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
170 DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
171 DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
172 DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
173 DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
174 DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
175 DDOC_SECTION_H = $(B $0)$(BR)\n\
176 DDOC_SECTION = $0$(BR)$(BR)\n\
177 DDOC_MEMBERS = $(DL $0)\n\
178 DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
179 DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\
180 DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
181 DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\
182 DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
183 DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
184 DDOC_PARAM_ROW = $(TR $0)\n\
185 DDOC_PARAM_ID = $(TD $0)\n\
186 DDOC_PARAM_DESC = $(TD $0)\n\
187 DDOC_BLANKLINE = $(BR)$(BR)\n\
189 DDOC_PSYMBOL = $(U $0)\n\
190 DDOC_KEYWORD = $(B $0)\n\
191 DDOC_PARAM = $(I $0)\n\
193 ESCAPES = /</</\n\
198 static char ddoc_decl_s
[] = "$(DDOC_DECL ";
199 static char ddoc_decl_e
[] = ")\n";
201 static char ddoc_decl_dd_s
[] = "$(DDOC_DECL_DD ";
202 static char ddoc_decl_dd_e
[] = ")\n";
205 /****************************************************
208 void Module::gendocfile()
210 static OutBuffer mbuf
;
211 static int mbuf_done
;
215 //printf("Module::gendocfile()\n");
217 if (!mbuf_done
) // if not already read the ddoc files
220 // Use our internal default
221 mbuf
.write(ddoc_default
, sizeof(ddoc_default
) - 1);
223 // Override with DDOCFILE specified in the sc.ini file
224 char *p
= getenv("DDOCFILE");
226 global
.params
.ddocfiles
->shift(p
);
228 // Override with the ddoc macro files from the command line
229 for (int i
= 0; i
< global
.params
.ddocfiles
->dim
; i
++)
231 FileName
f((char *)global
.params
.ddocfiles
->data
[i
], 0);
234 // BUG: convert file contents to UTF-8 before use
236 //printf("file: '%.*s'\n", file.len, file.buffer);
237 mbuf
.write(file
.buffer
, file
.len
);
240 DocComment::parseMacros(&escapetable
, ¯otable
, mbuf
.data
, mbuf
.offset
);
242 Scope
*sc
= Scope::createGlobal(this); // create root scope
245 DocComment
*dc
= DocComment::parse(sc
, this, comment
);
246 dc
->pmacrotable
= ¯otable
;
247 dc
->pescapetable
= &escapetable
;
249 // Generate predefined macros
251 // Set the title to be the name of the module
252 { char *p
= toPrettyChars();
253 Macro::define(¯otable
, (unsigned char *)"TITLE", 5, (unsigned char *)p
, strlen(p
));
260 Macro::define(¯otable
, (unsigned char *)"DATETIME", 8, (unsigned char *)p
, strlen(p
));
261 Macro::define(¯otable
, (unsigned char *)"YEAR", 4, (unsigned char *)p
+ 20, 4);
263 char *docfilename
= docfile
->toChars();
264 Macro::define(¯otable
, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename
, strlen(docfilename
));
268 dc
->copyright
->nooutput
= 1;
269 Macro::define(¯otable
, (unsigned char *)"COPYRIGHT", 9, dc
->copyright
->body
, dc
->copyright
->bodylen
);
272 buf
.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile
->toChars());
275 size_t commentlen
= strlen((char *)comment
);
278 commentlen
= dc
->macros
->name
- comment
;
279 dc
->macros
->write(dc
, sc
, this, sc
->docbuf
);
281 sc
->docbuf
->write(comment
, commentlen
);
282 highlightText(NULL
, this, sc
->docbuf
, 0);
286 dc
->writeSections(sc
, this, sc
->docbuf
);
287 emitMemberComments(sc
);
290 //printf("BODY= '%.*s'\n", buf.offset, buf.data);
291 Macro::define(¯otable
, (unsigned char *)"BODY", 4, buf
.data
, buf
.offset
);
294 buf2
.writestring("$(DDOC)\n");
295 unsigned end
= buf2
.offset
;
296 macrotable
->expand(&buf2
, 0, &end
, NULL
, 0);
299 /* Remove all the escape sequences from buf2,
300 * and make CR-LF the newline.
304 buf
.reserve(buf2
.offset
);
305 unsigned char *p
= buf2
.data
;
306 for (unsigned j
= 0; j
< buf2
.offset
; j
++)
308 unsigned char c
= p
[j
];
309 if (c
== 0xFF && j
+ 1 < buf2
.offset
)
318 buf
.writestring("\r\n");
319 if (j
+ 1 < buf2
.offset
&& p
[j
+ 1] == '\n')
329 // Transfer image to file
331 docfile
->setbuffer(buf
.data
, buf
.offset
);
333 char *pt
= FileName::path(docfile
->toChars());
335 FileName::ensurePathExists(pt
);
339 /* Remove all the escape sequences from buf2
342 unsigned char *p
= buf2
.data
;
343 for (unsigned j
= 0; j
< buf2
.offset
; j
++)
345 if (p
[j
] == 0xFF && j
+ 1 < buf2
.offset
)
356 // Transfer image to file
357 docfile
->setbuffer(buf2
.data
, buf2
.offset
);
359 char *pt
= FileName::path(docfile
->toChars());
361 FileName::ensurePathExists(pt
);
367 /******************************* emitComment **********************************/
370 * Emit doc comment to documentation file
373 void Dsymbol::emitDitto(Scope
*sc
)
375 //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars());
376 OutBuffer
*buf
= sc
->docbuf
;
380 b
.writestring("$(DDOC_DITTO ");
383 //printf("b: '%.*s'\n", b.offset, b.data);
384 /* If 'this' is a function template, then highlightCode() was
385 * already run by FuncDeclaration::toDocbuffer().
387 TemplateDeclaration
*td
;
389 (td
= parent
->isTemplateDeclaration()) != NULL
&&
390 td
->onemember
== this)
394 highlightCode(sc
, this, &b
, o
);
396 buf
->spread(sc
->lastoffset
, b
.offset
);
397 memcpy(buf
->data
+ sc
->lastoffset
, b
.data
, b
.offset
);
398 sc
->lastoffset
+= b
.offset
;
401 void ScopeDsymbol::emitMemberComments(Scope
*sc
)
403 //printf("ScopeDsymbol::emitMemberComments()\n");
404 OutBuffer
*buf
= sc
->docbuf
;
407 { char *m
= "$(DDOC_MEMBERS \n";
410 m
= "$(DDOC_MODULE_MEMBERS \n";
411 else if (isClassDeclaration())
412 m
= "$(DDOC_CLASS_MEMBERS \n";
413 else if (isStructDeclaration())
414 m
= "$(DDOC_STRUCT_MEMBERS \n";
415 else if (isEnumDeclaration())
416 m
= "$(DDOC_ENUM_MEMBERS \n";
417 else if (isTemplateDeclaration())
418 m
= "$(DDOC_TEMPLATE_MEMBERS \n";
420 // BUG: if no members are actually printed, we should not emit DDOC_MEMBERS
423 for (int i
= 0; i
< members
->dim
; i
++)
425 Dsymbol
*s
= (Dsymbol
*)members
->data
[i
];
426 //printf("\ts = '%s'\n", s->toChars());
430 buf
->writestring(")\n");
434 void emitProtection(OutBuffer
*buf
, PROT prot
)
440 case PROTpackage
: p
= "package"; break;
441 case PROTprotected
: p
= "protected"; break;
442 case PROTexport
: p
= "export"; break;
443 default: p
= NULL
; break;
446 buf
->printf("%s ", p
);
449 void Dsymbol::emitComment(Scope
*sc
) { }
450 void InvariantDeclaration::emitComment(Scope
*sc
) { }
451 void PostBlitDeclaration::emitComment(Scope
*sc
) { }
452 void DtorDeclaration::emitComment(Scope
*sc
) { }
453 void StaticCtorDeclaration::emitComment(Scope
*sc
) { }
454 void StaticDtorDeclaration::emitComment(Scope
*sc
) { }
455 void ClassInfoDeclaration::emitComment(Scope
*sc
) { }
456 void ModuleInfoDeclaration::emitComment(Scope
*sc
) { }
457 void TypeInfoDeclaration::emitComment(Scope
*sc
) { }
460 void Declaration::emitComment(Scope
*sc
)
462 //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
463 //printf("type = %p\n", type);
465 if (protection
== PROTprivate
|| !ident
||
466 (!type
&& !isCtorDeclaration() && !isAliasDeclaration()))
471 OutBuffer
*buf
= sc
->docbuf
;
472 DocComment
*dc
= DocComment::parse(sc
, this, comment
);
480 dc
->pmacrotable
= &sc
->module
->macrotable
;
482 buf
->writestring(ddoc_decl_s
);
485 highlightCode(sc
, this, buf
, o
);
486 sc
->lastoffset
= buf
->offset
;
487 buf
->writestring(ddoc_decl_e
);
489 buf
->writestring(ddoc_decl_dd_s
);
490 dc
->writeSections(sc
, this, buf
);
491 buf
->writestring(ddoc_decl_dd_e
);
494 void AggregateDeclaration::emitComment(Scope
*sc
)
496 //printf("AggregateDeclaration::emitComment() '%s'\n", toChars());
497 if (prot() == PROTprivate
)
502 OutBuffer
*buf
= sc
->docbuf
;
503 DocComment
*dc
= DocComment::parse(sc
, this, comment
);
510 dc
->pmacrotable
= &sc
->module
->macrotable
;
512 buf
->writestring(ddoc_decl_s
);
514 sc
->lastoffset
= buf
->offset
;
515 buf
->writestring(ddoc_decl_e
);
517 buf
->writestring(ddoc_decl_dd_s
);
518 dc
->writeSections(sc
, this, buf
);
519 emitMemberComments(sc
);
520 buf
->writestring(ddoc_decl_dd_e
);
523 void TemplateDeclaration::emitComment(Scope
*sc
)
525 //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind());
526 if (prot() == PROTprivate
)
531 OutBuffer
*buf
= sc
->docbuf
;
532 DocComment
*dc
= DocComment::parse(sc
, this, comment
);
540 ss
= onemember
->isAggregateDeclaration();
543 ss
= onemember
->isFuncDeclaration();
556 dc
->pmacrotable
= &sc
->module
->macrotable
;
558 buf
->writestring(ddoc_decl_s
);
560 ss
->toDocBuffer(buf
);
562 highlightCode(sc
, this, buf
, o
);
563 sc
->lastoffset
= buf
->offset
;
564 buf
->writestring(ddoc_decl_e
);
566 buf
->writestring(ddoc_decl_dd_s
);
567 dc
->writeSections(sc
, this, buf
);
569 ((ScopeDsymbol
*)ss
)->emitMemberComments(sc
);
570 buf
->writestring(ddoc_decl_dd_e
);
573 void EnumDeclaration::emitComment(Scope
*sc
)
575 if (prot() == PROTprivate
)
578 { if (isAnonymous() && members
)
580 for (int i
= 0; i
< members
->dim
; i
++)
582 Dsymbol
*s
= (Dsymbol
*)members
->data
[i
];
593 OutBuffer
*buf
= sc
->docbuf
;
594 DocComment
*dc
= DocComment::parse(sc
, this, comment
);
601 dc
->pmacrotable
= &sc
->module
->macrotable
;
603 buf
->writestring(ddoc_decl_s
);
605 sc
->lastoffset
= buf
->offset
;
606 buf
->writestring(ddoc_decl_e
);
608 buf
->writestring(ddoc_decl_dd_s
);
609 dc
->writeSections(sc
, this, buf
);
610 emitMemberComments(sc
);
611 buf
->writestring(ddoc_decl_dd_e
);
614 void EnumMember::emitComment(Scope
*sc
)
616 //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
617 if (prot() == PROTprivate
)
622 OutBuffer
*buf
= sc
->docbuf
;
623 DocComment
*dc
= DocComment::parse(sc
, this, comment
);
631 dc
->pmacrotable
= &sc
->module
->macrotable
;
633 buf
->writestring(ddoc_decl_s
);
636 highlightCode(sc
, this, buf
, o
);
637 sc
->lastoffset
= buf
->offset
;
638 buf
->writestring(ddoc_decl_e
);
640 buf
->writestring(ddoc_decl_dd_s
);
641 dc
->writeSections(sc
, this, buf
);
642 buf
->writestring(ddoc_decl_dd_e
);
645 /******************************* toDocBuffer **********************************/
647 void Dsymbol::toDocBuffer(OutBuffer
*buf
)
649 //printf("Dsymbol::toDocbuffer() %s\n", toChars());
653 toCBuffer(buf
, &hgs
);
656 void prefix(OutBuffer
*buf
, Dsymbol
*s
)
658 if (s
->isDeprecated())
659 buf
->writestring("deprecated ");
660 Declaration
*d
= s
->isDeclaration();
663 emitProtection(buf
, d
->protection
);
665 buf
->writestring("abstract ");
667 buf
->writestring("static ");
669 buf
->writestring("const ");
671 if (d
->isInvariant())
672 buf
->writestring("invariant ");
675 buf
->writestring("final ");
676 if (d
->isSynchronized())
677 buf
->writestring("synchronized ");
681 void Declaration::toDocBuffer(OutBuffer
*buf
)
683 //printf("Declaration::toDocbuffer() %s, originalType = %p\n", toChars(), originalType);
692 { //originalType->print();
693 originalType
->toCBuffer(buf
, ident
, &hgs
);
696 type
->toCBuffer(buf
, ident
, &hgs
);
699 buf
->writestring(ident
->toChars());
700 buf
->writestring(";\n");
705 void AliasDeclaration::toDocBuffer(OutBuffer
*buf
)
707 //printf("AliasDeclaration::toDocbuffer() %s\n", toChars());
711 buf
->writestring("deprecated ");
713 emitProtection(buf
, protection
);
714 buf
->writestring("alias ");
715 buf
->writestring(toChars());
716 buf
->writestring(";\n");
721 void TypedefDeclaration::toDocBuffer(OutBuffer
*buf
)
726 buf
->writestring("deprecated ");
728 emitProtection(buf
, protection
);
729 buf
->writestring("typedef ");
730 buf
->writestring(toChars());
731 buf
->writestring(";\n");
736 void FuncDeclaration::toDocBuffer(OutBuffer
*buf
)
738 //printf("FuncDeclaration::toDocbuffer() %s\n", toChars());
741 TemplateDeclaration
*td
;
744 (td
= parent
->isTemplateDeclaration()) != NULL
&&
745 td
->onemember
== this)
747 unsigned o
= buf
->offset
;
748 TypeFunction
*tf
= (TypeFunction
*)type
;
752 tf
->next
->toCBuffer(buf
, NULL
, &hgs
);
754 buf
->writestring(ident
->toChars());
756 for (int i
= 0; i
< td
->origParameters
->dim
; i
++)
758 TemplateParameter
*tp
= (TemplateParameter
*)td
->origParameters
->data
[i
];
761 tp
->toCBuffer(buf
, &hgs
);
764 Argument::argsToCBuffer(buf
, &hgs
, tf
->parameters
, tf
->varargs
);
765 buf
->writestring(";\n");
767 highlightCode(NULL
, this, buf
, o
);
771 Declaration::toDocBuffer(buf
);
776 void CtorDeclaration::toDocBuffer(OutBuffer
*buf
)
780 buf
->writestring("this");
781 Argument::argsToCBuffer(buf
, &hgs
, arguments
, varargs
);
782 buf
->writestring(";\n");
786 void AggregateDeclaration::toDocBuffer(OutBuffer
*buf
)
791 emitProtection(buf
, protection
);
793 buf
->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
794 buf
->writestring(";\n");
798 void StructDeclaration::toDocBuffer(OutBuffer
*buf
)
800 //printf("StructDeclaration::toDocbuffer() %s\n", toChars());
804 emitProtection(buf
, protection
);
806 TemplateDeclaration
*td
;
809 (td
= parent
->isTemplateDeclaration()) != NULL
&&
810 td
->onemember
== this)
811 { unsigned o
= buf
->offset
;
812 td
->toDocBuffer(buf
);
813 highlightCode(NULL
, this, buf
, o
);
817 buf
->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
819 buf
->writestring(";\n");
823 void ClassDeclaration::toDocBuffer(OutBuffer
*buf
)
825 //printf("ClassDeclaration::toDocbuffer() %s\n", toChars());
829 emitProtection(buf
, protection
);
831 TemplateDeclaration
*td
;
834 (td
= parent
->isTemplateDeclaration()) != NULL
&&
835 td
->onemember
== this)
836 { unsigned o
= buf
->offset
;
837 td
->toDocBuffer(buf
);
838 highlightCode(NULL
, this, buf
, o
);
842 buf
->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
845 for (int i
= 0; i
< baseclasses
.dim
; i
++)
846 { BaseClass
*bc
= (BaseClass
*)baseclasses
.data
[i
];
848 if (bc
->protection
== PROTprivate
)
850 if (bc
->base
&& bc
->base
->ident
== Id::Object
)
854 buf
->writestring(", ");
856 { buf
->writestring(": ");
859 emitProtection(buf
, bc
->protection
);
862 buf
->writestring(bc
->base
->toPrettyChars());
867 bc
->type
->toCBuffer(buf
, NULL
, &hgs
);
870 buf
->writestring(";\n");
875 void EnumDeclaration::toDocBuffer(OutBuffer
*buf
)
879 buf
->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
880 buf
->writestring(";\n");
884 void EnumMember::toDocBuffer(OutBuffer
*buf
)
888 buf
->writestring(toChars());
893 /********************************* DocComment *********************************/
895 DocComment::DocComment()
897 memset(this, 0, sizeof(DocComment
));
900 DocComment
*DocComment::parse(Scope
*sc
, Dsymbol
*s
, unsigned char *comment
)
903 if (sc
->lastdc
&& isDitto(comment
))
906 DocComment
*dc
= new DocComment();
910 dc
->parseSections(comment
);
912 for (int i
= 0; i
< dc
->sections
.dim
; i
++)
913 { Section
*s
= (Section
*)dc
->sections
.data
[i
];
915 if (icmp("copyright", s
->name
, s
->namelen
) == 0)
919 if (icmp("macros", s
->name
, s
->namelen
) == 0)
929 /*****************************************
930 * Parse next paragraph out of *pcomment.
931 * Update *pcomment to point past paragraph.
932 * Returns NULL if no more paragraphs.
933 * If paragraph ends in 'identifier:',
934 * then (*pcomment)[0 .. idlen] is the identifier.
937 void DocComment::parseSections(unsigned char *comment
)
939 unsigned char *pstart
;
942 unsigned char *idstart
;
945 unsigned char *name
= NULL
;
946 unsigned namelen
= 0;
951 p
= skipwhitespace(p
);
954 /* Find end of section, which is ended by one of:
961 if (isalpha(*p
) || *p
== '_')
964 while (isalnum(*q
) || *q
== '_')
966 if (*q
== ':') // identifier: ends it
969 for (pend
= p
; pend
> pstart
; pend
--)
970 { if (pend
[-1] == '\n')
985 if (*p
== '\n' && !summary
&& !namelen
)
995 p
= skipwhitespace(p
);
999 if (namelen
|| pstart
< pend
)
1002 if (icmp("Params", name
, namelen
) == 0)
1003 s
= new ParamSection();
1004 else if (icmp("Macros", name
, namelen
) == 0)
1005 s
= new MacroSection();
1009 s
->namelen
= namelen
;
1011 s
->bodylen
= pend
- pstart
;
1014 //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
1018 if (!summary
&& !namelen
)
1035 void DocComment::writeSections(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
)
1037 //printf("DocComment::writeSections()\n");
1040 buf
->writestring("$(DDOC_SECTIONS \n");
1041 for (int i
= 0; i
< sections
.dim
; i
++)
1042 { Section
*sec
= (Section
*)sections
.data
[i
];
1046 //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
1047 if (sec
->namelen
|| i
)
1048 sec
->write(this, sc
, s
, buf
);
1051 buf
->writestring("$(DDOC_SUMMARY ");
1052 unsigned o
= buf
->offset
;
1053 buf
->write(sec
->body
, sec
->bodylen
);
1054 highlightText(sc
, s
, buf
, o
);
1055 buf
->writestring(")\n");
1058 buf
->writestring(")\n");
1062 buf
->writestring("$(DDOC_BLANKLINE)\n");
1066 /***************************************************
1069 void Section::write(DocComment
*dc
, Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
)
1073 static char *table
[] =
1074 { "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
1075 "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
1076 "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
1079 for (int i
= 0; i
< sizeof(table
) / sizeof(table
[0]); i
++)
1081 if (icmp(table
[i
], name
, namelen
) == 0)
1083 buf
->printf("$(DDOC_%s ", table
[i
]);
1088 buf
->writestring("$(DDOC_SECTION ");
1089 // Replace _ characters with spaces
1090 buf
->writestring("$(DDOC_SECTION_H ");
1091 for (unsigned u
= 0; u
< namelen
; u
++)
1092 { unsigned char c
= name
[u
];
1093 buf
->writeByte((c
== '_') ? ' ' : c
);
1095 buf
->writestring(":)\n");
1099 buf
->writestring("$(DDOC_DESCRIPTION ");
1102 unsigned o
= buf
->offset
;
1103 buf
->write(body
, bodylen
);
1104 highlightText(sc
, s
, buf
, o
);
1105 buf
->writestring(")\n");
1108 /***************************************************
1111 void ParamSection::write(DocComment
*dc
, Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
)
1113 unsigned char *p
= body
;
1114 unsigned len
= bodylen
;
1115 unsigned char *pend
= p
+ len
;
1117 unsigned char *tempstart
;
1120 unsigned char *namestart
;
1121 unsigned namelen
= 0; // !=0 if line continuation
1123 unsigned char *textstart
;
1129 buf
->writestring("$(DDOC_PARAMS \n");
1132 // Skip to start of macro
1146 if (!(isalpha(*p
) || *p
== '_'))
1149 goto Ltext
; // continuation of prev macro
1158 while (isalnum(*p
) || *p
== '_')
1160 templen
= p
- tempstart
;
1162 while (*p
== ' ' || *p
== '\t')
1167 goto Ltext
; // continuation of prev macro
1173 { // Output existing param
1176 //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1178 buf
->writestring("$(DDOC_PARAM_ROW ");
1179 buf
->writestring("$(DDOC_PARAM_ID ");
1181 arg
= isFunctionParameter(s
, namestart
, namelen
);
1182 if (arg
&& arg
->type
&& arg
->ident
)
1183 arg
->type
->toCBuffer(buf
, arg
->ident
, &hgs
);
1185 buf
->write(namestart
, namelen
);
1186 highlightCode(sc
, s
, buf
, o
);
1187 buf
->writestring(")\n");
1189 buf
->writestring("$(DDOC_PARAM_DESC ");
1191 buf
->write(textstart
, textlen
);
1192 highlightText(sc
, s
, buf
, o
);
1193 buf
->writestring(")");
1194 buf
->writestring(")\n");
1200 namestart
= tempstart
;
1203 while (*p
== ' ' || *p
== '\t')
1210 textlen
= p
- textstart
;
1218 while (*p
++ != '\n')
1222 goto L1
; // write out last one
1223 buf
->writestring(")\n");
1226 /***************************************************
1229 void MacroSection::write(DocComment
*dc
, Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
)
1231 //printf("MacroSection::write()\n");
1232 DocComment::parseMacros(dc
->pescapetable
, dc
->pmacrotable
, body
, bodylen
);
1235 /************************************************
1236 * Parse macros out of Macros: section.
1237 * Macros are of the form:
1243 void DocComment::parseMacros(Escape
**pescapetable
, Macro
**pmacrotable
, unsigned char *m
, unsigned mlen
)
1245 unsigned char *p
= m
;
1246 unsigned len
= mlen
;
1247 unsigned char *pend
= p
+ len
;
1249 unsigned char *tempstart
;
1252 unsigned char *namestart
;
1253 unsigned namelen
= 0; // !=0 if line continuation
1255 unsigned char *textstart
;
1260 // Skip to start of macro
1276 if (!(isalpha(*p
) || *p
== '_'))
1279 goto Ltext
; // continuation of prev macro
1292 if (!(isalnum(*p
) || *p
== '_'))
1296 templen
= p
- tempstart
;
1302 if (!(*p
== ' ' || *p
== '\t'))
1309 goto Ltext
; // continuation of prev macro
1317 { // Output existing macro
1319 //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1320 if (icmp("ESCAPES", namestart
, namelen
) == 0)
1321 parseEscapes(pescapetable
, textstart
, textlen
);
1323 Macro::define(pmacrotable
, namestart
, namelen
, textstart
, textlen
);
1329 namestart
= tempstart
;
1332 while (p
< pend
&& (*p
== ' ' || *p
== '\t'))
1337 while (p
< pend
&& *p
!= '\n')
1339 textlen
= p
- textstart
;
1341 // Remove trailing \r if there is one
1342 if (p
> m
&& p
[-1] == '\r')
1346 //printf("p = %p, pend = %p\n", p, pend);
1353 while (p
< pend
&& *p
++ != '\n')
1358 goto L1
; // write out last one
1361 /**************************************
1362 * Parse escapes of the form:
1364 * where c is a single character.
1365 * Multiple escapes can be separated
1366 * by whitespace and/or commas.
1369 void DocComment::parseEscapes(Escape
**pescapetable
, unsigned char *textstart
, unsigned textlen
)
1370 { Escape
*escapetable
= *pescapetable
;
1373 { escapetable
= new Escape
;
1374 *pescapetable
= escapetable
;
1376 unsigned char *p
= textstart
;
1377 unsigned char *pend
= p
+ textlen
;
1385 if (!(*p
== ' ' || *p
== '\t' || *p
== '\n' || *p
== ','))
1389 if (p
[0] != '/' || p
[2] != '/')
1391 unsigned char c
= p
[1];
1393 unsigned char *start
= p
;
1402 size_t len
= p
- start
;
1403 char *s
= (char *)memcpy(mem
.malloc(len
+ 1), start
, len
);
1405 escapetable
->strings
[c
] = s
;
1406 //printf("%c = '%s'\n", c, s);
1412 /******************************************
1413 * Compare 0-terminated string with length terminated string.
1414 * Return < 0, ==0, > 0
1417 int cmp(char *stringz
, void *s
, size_t slen
)
1419 size_t len1
= strlen(stringz
);
1423 return memcmp(stringz
, s
, slen
);
1426 int icmp(char *stringz
, void *s
, size_t slen
)
1428 size_t len1
= strlen(stringz
);
1432 return memicmp(stringz
, (char *)s
, slen
);
1435 /*****************************************
1436 * Return !=0 if comment consists entirely of "ditto".
1439 int isDitto(unsigned char *comment
)
1443 unsigned char *p
= skipwhitespace(comment
);
1445 if (memicmp((char *)p
, "ditto", 5) == 0 && *skipwhitespace(p
+ 5) == 0)
1451 /**********************************************
1455 unsigned char *skipwhitespace(unsigned char *p
)
1471 /************************************************
1472 * Scan forward to one of:
1473 * start of identifier
1474 * beginning of next line
1478 unsigned skiptoident(OutBuffer
*buf
, unsigned i
)
1480 for (; i
< buf
->offset
; i
++)
1482 // BUG: handle unicode alpha's
1483 unsigned char c
= buf
->data
[i
];
1484 if (isalpha(c
) || c
== '_')
1492 /************************************************
1493 * Scan forward past end of identifier.
1496 unsigned skippastident(OutBuffer
*buf
, unsigned i
)
1498 for (; i
< buf
->offset
; i
++)
1500 // BUG: handle unicode alpha's
1501 unsigned char c
= buf
->data
[i
];
1502 if (!(isalnum(c
) || c
== '_'))
1509 /************************************************
1510 * Scan forward past URL starting at i.
1511 * We don't want to highlight parts of a URL.
1514 * index just past it if it is a URL
1517 unsigned skippastURL(OutBuffer
*buf
, unsigned i
)
1518 { unsigned length
= buf
->offset
- i
;
1519 unsigned char *p
= &buf
->data
[i
];
1521 unsigned sawdot
= 0;
1523 if (length
> 7 && memicmp((char *)p
, "http://", 7) == 0)
1527 else if (length
> 8 && memicmp((char *)p
, "https://", 8) == 0)
1534 for (; j
< length
; j
++)
1535 { unsigned char c
= p
[j
];
1538 if (c
== '-' || c
== '_' || c
== '?' ||
1539 c
== '=' || c
== '%' || c
== '&' ||
1540 c
== '/' || c
== '+' || c
== '#' ||
1558 /****************************************************
1561 int isKeyword(unsigned char *p
, unsigned len
)
1563 static char *table
[] = { "true", "false", "null" };
1565 for (int i
= 0; i
< sizeof(table
) / sizeof(table
[0]); i
++)
1567 if (cmp(table
[i
], p
, len
) == 0)
1573 /****************************************************
1576 Argument
*isFunctionParameter(Dsymbol
*s
, unsigned char *p
, unsigned len
)
1578 FuncDeclaration
*f
= s
->isFuncDeclaration();
1580 /* f->type may be NULL for template members.
1584 TypeFunction
*tf
= (TypeFunction
*)f
->type
;
1588 for (size_t k
= 0; k
< tf
->parameters
->dim
; k
++)
1589 { Argument
*arg
= (Argument
*)tf
->parameters
->data
[k
];
1591 if (arg
->ident
&& cmp(arg
->ident
->toChars(), p
, len
) == 0)
1601 /**************************************************
1602 * Highlight text section.
1605 void highlightText(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, unsigned offset
)
1607 //printf("highlightText()\n");
1608 char *sid
= s
->ident
->toChars();
1609 FuncDeclaration
*f
= s
->isFuncDeclaration();
1613 int leadingBlank
= 1;
1615 int inComment
= 0; // in <!-- ... --> comment
1616 unsigned iCodeStart
; // start of code section
1618 unsigned iLineStart
= offset
;
1620 for (unsigned i
= offset
; i
< buf
->offset
; i
++)
1621 { unsigned char c
= buf
->data
[i
];
1631 if (sc
&& !inCode
&& i
== iLineStart
&& i
+ 1 < buf
->offset
) // if "\n\n"
1633 static char blankline
[] = "$(DDOC_BLANKLINE)\n";
1635 i
= buf
->insert(i
, blankline
, sizeof(blankline
) - 1);
1647 // Skip over comments
1648 if (p
[1] == '!' && p
[2] == '-' && p
[3] == '-')
1649 { unsigned j
= i
+ 4;
1653 if (j
== buf
->offset
)
1655 if (p
[0] == '-' && p
[1] == '-' && p
[2] == '>')
1657 i
= j
+ 2; // place on closing '>'
1666 // Skip over HTML tag
1667 if (isalpha(p
[1]) || (p
[1] == '/' && isalpha(p
[2])))
1668 { unsigned j
= i
+ 2;
1672 if (j
== buf
->offset
)
1676 i
= j
; // place on closing '>'
1686 // Replace '<' with '<' character entity
1687 se
= Escape::escapeChar('<');
1689 { size_t len
= strlen(se
);
1691 i
= buf
->insert(i
, se
, len
);
1692 i
--; // point to ';'
1700 // Replace '>' with '>' character entity
1701 se
= Escape::escapeChar('>');
1703 { size_t len
= strlen(se
);
1705 i
= buf
->insert(i
, se
, len
);
1706 i
--; // point to ';'
1715 if (p
[1] == '#' || isalpha(p
[1]))
1716 break; // already a character entity
1717 // Replace '&' with '&' character entity
1718 se
= Escape::escapeChar('&');
1720 { size_t len
= strlen(se
);
1722 i
= buf
->insert(i
, se
, len
);
1723 i
--; // point to ';'
1728 /* A line beginning with --- delimits a code section.
1729 * inCode tells us if it is start or end of a code section.
1739 if (i
>= buf
->offset
)
1749 if (i
+ 1 >= buf
->offset
)
1751 if (buf
->data
[i
+ 1] == '\n')
1756 // BUG: handle UTF PS and LS too
1763 // We have the start/end of a code section
1765 // Remove the entire --- line, including blanks and \n
1766 buf
->remove(iLineStart
, i
- iLineStart
+ eollen
);
1772 // The code section is from iCodeStart to i
1775 codebuf
.write(buf
->data
+ iCodeStart
, i
- iCodeStart
);
1776 codebuf
.writeByte(0);
1777 highlightCode2(sc
, s
, &codebuf
, 0);
1778 buf
->remove(iCodeStart
, i
- iCodeStart
);
1779 i
= buf
->insert(iCodeStart
, codebuf
.data
, codebuf
.offset
);
1780 i
= buf
->insert(i
, ")\n", 2);
1784 { static char pre
[] = "$(D_CODE \n";
1787 i
= buf
->insert(i
, pre
, sizeof(pre
) - 1);
1789 i
--; // place i on >
1796 if (sc
&& !inCode
&& (isalpha(c
) || c
== '_'))
1799 j
= skippastident(buf
, i
);
1802 unsigned k
= skippastURL(buf
, i
);
1808 if (buf
->data
[i
] == '_') // leading '_' means no highlight
1815 if (cmp(sid
, buf
->data
+ i
, j
- i
) == 0)
1817 i
= buf
->bracket(i
, "$(DDOC_PSYMBOL ", j
, ")") - 1;
1820 else if (isKeyword(buf
->data
+ i
, j
- i
))
1822 i
= buf
->bracket(i
, "$(DDOC_KEYWORD ", j
, ")") - 1;
1827 if (f
&& isFunctionParameter(f
, buf
->data
+ i
, j
- i
))
1829 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
1830 i
= buf
->bracket(i
, "$(DDOC_PARAM ", j
, ")") - 1;
1845 /**************************************************
1846 * Highlight code for DDOC section.
1849 void highlightCode(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, unsigned offset
)
1851 char *sid
= s
->ident
->toChars();
1852 FuncDeclaration
*f
= s
->isFuncDeclaration();
1854 //printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind());
1855 for (unsigned i
= offset
; i
< buf
->offset
; i
++)
1856 { unsigned char c
= buf
->data
[i
];
1859 se
= Escape::escapeChar(c
);
1862 size_t len
= strlen(se
);
1864 i
= buf
->insert(i
, se
, len
);
1865 i
--; // point to ';'
1867 else if (isalpha(c
) || c
== '_')
1870 j
= skippastident(buf
, i
);
1873 if (cmp(sid
, buf
->data
+ i
, j
- i
) == 0)
1875 i
= buf
->bracket(i
, "$(DDOC_PSYMBOL ", j
, ")") - 1;
1880 if (isFunctionParameter(f
, buf
->data
+ i
, j
- i
))
1882 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
1883 i
= buf
->bracket(i
, "$(DDOC_PARAM ", j
, ")") - 1;
1893 /****************************************
1896 void highlightCode3(OutBuffer
*buf
, unsigned char *p
, unsigned char *pend
)
1898 for (; p
< pend
; p
++)
1899 { char *s
= Escape::escapeChar(*p
);
1901 buf
->writestring(s
);
1907 /**************************************************
1908 * Highlight code for CODE section.
1912 void highlightCode2(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, unsigned offset
)
1914 char *sid
= s
->ident
->toChars();
1915 FuncDeclaration
*f
= s
->isFuncDeclaration();
1916 unsigned errorsave
= global
.errors
;
1917 Lexer
lex(NULL
, buf
->data
, 0, buf
->offset
- 1, 0, 1);
1920 unsigned char *lastp
= buf
->data
;
1923 //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data);
1924 res
.reserve(buf
->offset
);
1928 highlightCode3(&res
, lastp
, tok
.ptr
);
1935 if (cmp(sid
, tok
.ptr
, lex
.p
- tok
.ptr
) == 0)
1937 highlight
= "$(D_PSYMBOL ";
1942 if (isFunctionParameter(f
, tok
.ptr
, lex
.p
- tok
.ptr
))
1944 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
1945 highlight
= "$(D_PARAM ";
1952 highlight
= "$(D_COMMENT ";
1956 highlight
= "$(D_STRING ";
1960 if (tok
.isKeyword())
1961 highlight
= "$(D_KEYWORD ";
1965 res
.writestring(highlight
);
1966 highlightCode3(&res
, tok
.ptr
, lex
.p
);
1969 if (tok
.value
== TOKeof
)
1973 buf
->setsize(offset
);
1975 global
.errors
= errorsave
;
1978 /***************************************
1979 * Find character string to replace c with.
1982 char *Escape::escapeChar(unsigned c
)