a448e342a82b262b32e87583352f5f44c2528308
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/doc.c
11 // This implements the Ddoc capability.
13 #include "root/dsystem.h"
14 #include "root/rmem.h"
15 #include "root/root.h"
16 #include "root/port.h"
26 #include "aggregate.h"
27 #include "declaration.h"
28 #include "statement.h"
38 void emitMemberComments(ScopeDsymbol
*sds
, OutBuffer
*buf
, Scope
*sc
);
39 void toDocBuffer(Dsymbol
*s
, OutBuffer
*buf
, Scope
*sc
);
40 void emitComment(Dsymbol
*s
, OutBuffer
*buf
, Scope
*sc
);
44 const char *strings
[256];
46 const char *escapeChar(unsigned c
);
60 virtual void write(Loc loc
, DocComment
*dc
, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
);
63 class ParamSection
: public Section
66 void write(Loc loc
, DocComment
*dc
, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
);
69 class MacroSection
: public Section
72 void write(Loc loc
, DocComment
*dc
, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
);
75 typedef Array
<Section
*> Sections
;
79 Sections sections
; // Section*[]
85 Escape
**pescapetable
;
90 summary(NULL
), copyright(NULL
), macros(NULL
), pmacrotable(NULL
), pescapetable(NULL
)
93 static DocComment
*parse(Dsymbol
*s
, const utf8_t
*comment
);
94 static void parseMacros(Escape
**pescapetable
, Macro
**pmacrotable
, const utf8_t
*m
, size_t mlen
);
95 static void parseEscapes(Escape
**pescapetable
, const utf8_t
*textstart
, size_t textlen
);
97 void parseSections(const utf8_t
*comment
);
98 void writeSections(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
);
102 int cmp(const char *stringz
, const void *s
, size_t slen
);
103 int icmp(const char *stringz
, const void *s
, size_t slen
);
104 bool isDitto(const utf8_t
*comment
);
105 const utf8_t
*skipwhitespace(const utf8_t
*p
);
106 size_t skiptoident(OutBuffer
*buf
, size_t i
);
107 size_t skippastident(OutBuffer
*buf
, size_t i
);
108 size_t skippastURL(OutBuffer
*buf
, size_t i
);
109 void highlightText(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
);
110 void highlightCode(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, size_t offset
);
111 void highlightCode(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
);
112 void highlightCode2(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
);
113 void highlightCode3(Scope
*sc
, OutBuffer
*buf
, const utf8_t
*p
, const utf8_t
*pend
);
114 TypeFunction
*isTypeFunction(Dsymbol
*s
);
115 Parameter
*isFunctionParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
);
116 TemplateParameter
*isTemplateParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
);
118 bool isIdStart(const utf8_t
*p
);
119 bool isCVariadicArg(const utf8_t
*p
, size_t len
);
120 bool isIdTail(const utf8_t
*p
);
121 bool isIndentWS(const utf8_t
*p
);
122 int utfStride(const utf8_t
*p
);
124 // Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
125 bool isCVariadicParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
127 for (size_t i
= 0; i
< a
->length
; i
++)
129 TypeFunction
*tf
= isTypeFunction((*a
)[i
]);
130 if (tf
&& tf
->varargs
== 1 && cmp("...", p
, len
) == 0)
136 /****************************************************
138 static Parameter
*isFunctionParameter(Dsymbol
*s
, const utf8_t
*p
, size_t len
)
140 TypeFunction
*tf
= isTypeFunction(s
);
141 if (tf
&& tf
->parameters
)
143 for (size_t k
= 0; k
< tf
->parameters
->length
; k
++)
145 Parameter
*fparam
= (*tf
->parameters
)[k
];
146 if (fparam
->ident
&& cmp(fparam
->ident
->toChars(), p
, len
) == 0)
155 static Dsymbol
*getEponymousMember(TemplateDeclaration
*td
)
160 if (AggregateDeclaration
*ad
= td
->onemember
->isAggregateDeclaration())
162 if (FuncDeclaration
*fd
= td
->onemember
->isFuncDeclaration())
164 if (td
->onemember
->isEnumMember())
165 return NULL
; // Keep backward compatibility. See compilable/ddoc9.d
166 if (VarDeclaration
*vd
= td
->onemember
->isVarDeclaration())
167 return td
->constraint
? NULL
: vd
;
172 /****************************************************
174 static Parameter
*isEponymousFunctionParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
176 for (size_t i
= 0; i
< a
->length
; i
++)
178 TemplateDeclaration
*td
= (*a
)[i
]->isTemplateDeclaration();
179 if (td
&& td
->onemember
)
181 /* Case 1: we refer to a template declaration inside the template
188 td
= td
->onemember
->isTemplateDeclaration();
192 /* Case 2: we're an alias to a template declaration
195 alias case2 = case1!int;
197 AliasDeclaration
*ad
= (*a
)[i
]->isAliasDeclaration();
198 if (ad
&& ad
->aliassym
)
200 td
= ad
->aliassym
->isTemplateDeclaration();
205 Dsymbol
*sym
= getEponymousMember(td
);
208 Parameter
*fparam
= isFunctionParameter(sym
, p
, len
);
220 static TemplateDeclaration
*getEponymousParent(Dsymbol
*s
)
224 TemplateDeclaration
*td
= s
->parent
->isTemplateDeclaration();
225 return (td
&& getEponymousMember(td
)) ? td
: NULL
;
228 static const char ddoc_default
[] = "\
229 DDOC = <html><head>\n\
230 <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
231 <title>$(TITLE)</title>\n\
235 <hr>$(SMALL Page generated by $(LINK2 http://dlang.org/ddoc.html, Ddoc). $(COPYRIGHT))\n\
245 TABLE = <table>$0</table>\n\
252 BIG = <big>$0</big>\n\
253 SMALL = <small>$0</small>\n\
255 LINK = <a href=\"$0\">$0</a>\n\
256 LINK2 = <a href=\"$1\">$+</a>\n\
263 RED = <font color=red>$0</font>\n\
264 BLUE = <font color=blue>$0</font>\n\
265 GREEN = <font color=green>$0</font>\n\
266 YELLOW =<font color=yellow>$0</font>\n\
267 BLACK = <font color=black>$0</font>\n\
268 WHITE = <font color=white>$0</font>\n\
270 D_CODE = <pre class=\"d_code\">$0</pre>\n\
271 DDOC_BACKQUOTED = $(D_INLINECODE $0)\n\
272 D_INLINECODE = <pre style=\"display:inline;\" class=\"d_inline_code\">$0</pre>\n\
273 D_COMMENT = $(GREEN $0)\n\
274 D_STRING = $(RED $0)\n\
275 D_KEYWORD = $(BLUE $0)\n\
276 D_PSYMBOL = $(U $0)\n\
279 DDOC_COMMENT = <!-- $0 -->\n\
280 DDOC_DECL = $(DT $(BIG $0))\n\
281 DDOC_DECL_DD = $(DD $0)\n\
282 DDOC_DITTO = $(BR)$0\n\
283 DDOC_SECTIONS = $0\n\
284 DDOC_SUMMARY = $0$(BR)$(BR)\n\
285 DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
286 DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
287 DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
288 DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
289 DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
290 DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
291 DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
292 DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
293 DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
294 DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
295 DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
296 DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
297 DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
298 DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
299 DDOC_SECTION_H = $(B $0)$(BR)\n\
300 DDOC_SECTION = $0$(BR)$(BR)\n\
301 DDOC_MEMBERS = $(DL $0)\n\
302 DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
303 DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\
304 DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
305 DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\
306 DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
307 DDOC_ENUM_BASETYPE = $0\n\
308 DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
309 DDOC_PARAM_ROW = $(TR $0)\n\
310 DDOC_PARAM_ID = $(TD $0)\n\
311 DDOC_PARAM_DESC = $(TD $0)\n\
312 DDOC_BLANKLINE = $(BR)$(BR)\n\
314 DDOC_ANCHOR = <a name=\"$1\"></a>\n\
315 DDOC_PSYMBOL = $(U $0)\n\
316 DDOC_PSUPER_SYMBOL = $(U $0)\n\
317 DDOC_KEYWORD = $(B $0)\n\
318 DDOC_PARAM = $(I $0)\n\
320 ESCAPES = /</</\n\
325 static const char ddoc_decl_s
[] = "$(DDOC_DECL ";
326 static const char ddoc_decl_e
[] = ")\n";
328 static const char ddoc_decl_dd_s
[] = "$(DDOC_DECL_DD ";
329 static const char ddoc_decl_dd_e
[] = ")\n";
332 /****************************************************
335 void gendocfile(Module
*m
)
337 static OutBuffer mbuf
;
338 static int mbuf_done
;
342 //printf("Module::gendocfile()\n");
344 if (!mbuf_done
) // if not already read the ddoc files
348 // Use our internal default
349 mbuf
.write(ddoc_default
, strlen(ddoc_default
));
351 // Override with DDOCFILE specified in the sc.ini file
352 char *p
= getenv("DDOCFILE");
354 global
.params
.ddocfiles
->shift(p
);
356 // Override with the ddoc macro files from the command line
357 for (size_t i
= 0; i
< global
.params
.ddocfiles
->length
; i
++)
359 FileName
f((*global
.params
.ddocfiles
)[i
]);
361 readFile(m
->loc
, &file
);
362 // BUG: convert file contents to UTF-8 before use
364 //printf("file: '%.*s'\n", file.len, file.buffer);
365 mbuf
.write(file
.buffer
, file
.len
);
368 DocComment::parseMacros(&m
->escapetable
, &m
->macrotable
, (utf8_t
*)mbuf
.data
, mbuf
.offset
);
370 Scope
*sc
= Scope::createGlobal(m
); // create root scope
372 DocComment
*dc
= DocComment::parse(m
, m
->comment
);
373 dc
->pmacrotable
= &m
->macrotable
;
374 dc
->pescapetable
= &m
->escapetable
;
377 // Generate predefined macros
379 // Set the title to be the name of the module
381 const char *p
= m
->toPrettyChars();
382 Macro::define(&m
->macrotable
, (const utf8_t
*)"TITLE", 5, (const utf8_t
*)p
, strlen(p
));
391 Macro::define(&m
->macrotable
, (const utf8_t
*)"DATETIME", 8, (const utf8_t
*)p
, strlen(p
));
392 Macro::define(&m
->macrotable
, (const utf8_t
*)"YEAR", 4, (const utf8_t
*)p
+ 20, 4);
395 const char *srcfilename
= m
->srcfile
->toChars();
396 Macro::define(&m
->macrotable
, (const utf8_t
*)"SRCFILENAME", 11, (const utf8_t
*)srcfilename
, strlen(srcfilename
));
398 const char *docfilename
= m
->docfile
->toChars();
399 Macro::define(&m
->macrotable
, (const utf8_t
*)"DOCFILENAME", 11, (const utf8_t
*)docfilename
, strlen(docfilename
));
403 dc
->copyright
->nooutput
= 1;
404 Macro::define(&m
->macrotable
, (const utf8_t
*)"COPYRIGHT", 9, dc
->copyright
->body
, dc
->copyright
->bodylen
);
407 buf
.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", m
->srcfile
->toChars());
410 Loc loc
= m
->md
? m
->md
->loc
: m
->loc
;
411 size_t commentlen
= strlen((const char *)m
->comment
);
413 // Bugzilla 9764: Don't push m in a, to prevent emphasize ddoc file name.
416 commentlen
= dc
->macros
->name
- m
->comment
;
417 dc
->macros
->write(loc
, dc
, sc
, &a
, &buf
);
419 buf
.write(m
->comment
, commentlen
);
420 highlightText(sc
, &a
, &buf
, 0);
426 dc
->writeSections(sc
, &a
, &buf
);
427 emitMemberComments(m
, &buf
, sc
);
430 //printf("BODY= '%.*s'\n", buf.offset, buf.data);
431 Macro::define(&m
->macrotable
, (const utf8_t
*)"BODY", 4, (const utf8_t
*)buf
.data
, buf
.offset
);
434 buf2
.writestring("$(DDOC)\n");
435 size_t end
= buf2
.offset
;
436 m
->macrotable
->expand(&buf2
, 0, &end
, NULL
, 0);
438 /* Remove all the escape sequences from buf2,
439 * and make CR-LF the newline.
443 buf
.reserve(buf2
.offset
);
444 utf8_t
*p
= (utf8_t
*)buf2
.data
;
445 for (size_t j
= 0; j
< buf2
.offset
; j
++)
448 if (c
== 0xFF && j
+ 1 < buf2
.offset
)
457 buf
.writestring("\r\n");
458 if (j
+ 1 < buf2
.offset
&& p
[j
+ 1] == '\n')
468 // Transfer image to file
470 m
->docfile
->setbuffer(buf
.data
, buf
.offset
);
472 ensurePathToNameExists(Loc(), m
->docfile
->toChars());
473 writeFile(m
->loc
, m
->docfile
);
476 /****************************************************
477 * Having unmatched parentheses can hose the output of Ddoc,
478 * as the macros depend on properly nested parentheses.
479 * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
480 * to preserve text literally. This also means macros in the
481 * text won't be expanded.
483 void escapeDdocString(OutBuffer
*buf
, size_t start
)
485 for (size_t u
= start
; u
< buf
->offset
; u
++)
487 utf8_t c
= buf
->data
[u
];
492 buf
->insert(u
, (const char *)"$(DOLLAR)", 9);
497 buf
->remove(u
, 1); //remove the (
498 buf
->insert(u
, (const char *)"$(LPAREN)", 9); //insert this instead
499 u
+= 8; //skip over newly inserted macro
503 buf
->remove(u
, 1); //remove the )
504 buf
->insert(u
, (const char *)"$(RPAREN)", 9); //insert this instead
505 u
+= 8; //skip over newly inserted macro
511 /****************************************************
512 * Having unmatched parentheses can hose the output of Ddoc,
513 * as the macros depend on properly nested parentheses.
515 * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
517 void escapeStrayParenthesis(Loc loc
, OutBuffer
*buf
, size_t start
)
519 unsigned par_open
= 0;
521 for (size_t u
= start
; u
< buf
->offset
; u
++)
523 utf8_t c
= buf
->data
[u
];
534 warning(loc
, "Ddoc: Stray ')'. This may cause incorrect Ddoc output."
535 " Use $(RPAREN) instead for unpaired right parentheses.");
536 buf
->remove(u
, 1); //remove the )
537 buf
->insert(u
, (const char *)"$(RPAREN)", 9); //insert this instead
538 u
+= 8; //skip over newly inserted macro
546 if (par_open
) // if any unmatched lparens
549 for (size_t u
= buf
->offset
; u
> start
;)
552 utf8_t c
= buf
->data
[u
];
563 warning(loc
, "Ddoc: Stray '('. This may cause incorrect Ddoc output."
564 " Use $(LPAREN) instead for unpaired left parentheses.");
565 buf
->remove(u
, 1); //remove the (
566 buf
->insert(u
, (const char *)"$(LPAREN)", 9); //insert this instead
576 // Basically, this is to skip over things like private{} blocks in a struct or
577 // class definition that don't add any components to the qualified name.
578 static Scope
*skipNonQualScopes(Scope
*sc
)
580 while (sc
&& !sc
->scopesym
)
585 static bool emitAnchorName(OutBuffer
*buf
, Dsymbol
*s
, Scope
*sc
)
587 if (!s
|| s
->isPackage() || s
->isModule())
590 // Add parent names first
593 dot
= emitAnchorName(buf
, s
->parent
, sc
);
595 dot
= emitAnchorName(buf
, sc
->scopesym
, skipNonQualScopes(sc
->enclosing
));
597 // Eponymous template members can share the parent anchor name
598 if (getEponymousParent(s
))
603 // Use "this" not "__ctor"
604 TemplateDeclaration
*td
;
605 if (s
->isCtorDeclaration() || ((td
= s
->isTemplateDeclaration()) != NULL
&&
606 td
->onemember
&& td
->onemember
->isCtorDeclaration()))
608 buf
->writestring("this");
612 /* We just want the identifier, not overloads like TemplateDeclaration::toChars.
613 * We don't want the template parameter list and constraints. */
614 buf
->writestring(s
->Dsymbol::toChars());
619 static void emitAnchor(OutBuffer
*buf
, Dsymbol
*s
, Scope
*sc
)
624 emitAnchorName(&anc
, s
, skipNonQualScopes(sc
));
625 ident
= Identifier::idPool(anc
.peekString());
627 size_t *count
= (size_t*)dmd_aaGet(&sc
->anchorCounts
, (void *)ident
);
628 TemplateDeclaration
*td
= getEponymousParent(s
);
629 // don't write an anchor for matching consecutive ditto symbols
630 if (*count
> 0 && sc
->prevAnchor
== ident
&&
631 sc
->lastdc
&& (isDitto(s
->comment
) || (td
&& isDitto(td
->comment
))))
636 sc
->prevAnchor
= ident
;
638 buf
->writestring("$(DDOC_ANCHOR ");
639 buf
->writestring(ident
->toChars());
640 // only append count once there's a duplicate
642 buf
->printf(".%u", *count
);
646 /******************************* emitComment **********************************/
648 /** Get leading indentation from 'src' which represents lines of code. */
649 static size_t getCodeIndent(const char *src
)
651 while (src
&& (*src
== '\r' || *src
== '\n'))
652 ++src
; // skip until we find the first non-empty line
654 size_t codeIndent
= 0;
655 while (src
&& (*src
== ' ' || *src
== '\t'))
663 /** Recursively expand template mixin member docs into the scope. */
664 static void expandTemplateMixinComments(TemplateMixin
*tm
, OutBuffer
*buf
, Scope
*sc
)
666 if (!tm
->semanticRun
) tm
->semantic(sc
);
667 TemplateDeclaration
*td
= (tm
&& tm
->tempdecl
) ?
668 tm
->tempdecl
->isTemplateDeclaration() : NULL
;
669 if (td
&& td
->members
)
671 for (size_t i
= 0; i
< td
->members
->length
; i
++)
673 Dsymbol
*sm
= (*td
->members
)[i
];
674 TemplateMixin
*tmc
= sm
->isTemplateMixin();
675 if (tmc
&& tmc
->comment
)
676 expandTemplateMixinComments(tmc
, buf
, sc
);
678 emitComment(sm
, buf
, sc
);
683 void emitMemberComments(ScopeDsymbol
*sds
, OutBuffer
*buf
, Scope
*sc
)
688 //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
690 const char *m
= "$(DDOC_MEMBERS ";
691 if (sds
->isTemplateDeclaration())
692 m
= "$(DDOC_TEMPLATE_MEMBERS ";
693 else if (sds
->isClassDeclaration())
694 m
= "$(DDOC_CLASS_MEMBERS ";
695 else if (sds
->isStructDeclaration())
696 m
= "$(DDOC_STRUCT_MEMBERS ";
697 else if (sds
->isEnumDeclaration())
698 m
= "$(DDOC_ENUM_MEMBERS ";
699 else if (sds
->isModule())
700 m
= "$(DDOC_MODULE_MEMBERS ";
702 size_t offset1
= buf
->offset
; // save starting offset
704 size_t offset2
= buf
->offset
; // to see if we write anything
708 for (size_t i
= 0; i
< sds
->members
->length
; i
++)
710 Dsymbol
*s
= (*sds
->members
)[i
];
711 //printf("\ts = '%s'\n", s->toChars());
713 // only expand if parent is a non-template (semantic won't work)
714 if (s
->comment
&& s
->isTemplateMixin() && s
->parent
&& !s
->parent
->isTemplateDeclaration())
715 expandTemplateMixinComments((TemplateMixin
*)s
, buf
, sc
);
717 emitComment(s
, buf
, sc
);
719 emitComment(NULL
, buf
, sc
);
723 if (buf
->offset
== offset2
)
725 /* Didn't write out any members, so back out last write
727 buf
->offset
= offset1
;
730 buf
->writestring(")\n");
733 void emitProtection(OutBuffer
*buf
, Prot prot
)
735 if (prot
.kind
!= PROTundefined
&& prot
.kind
!= PROTpublic
)
737 protectionToBuffer(buf
, prot
);
742 void emitComment(Dsymbol
*s
, OutBuffer
*buf
, Scope
*sc
)
744 class EmitComment
: public Visitor
750 EmitComment(OutBuffer
*buf
, Scope
*sc
)
755 void visit(Dsymbol
*) {}
756 void visit(InvariantDeclaration
*) {}
757 void visit(UnitTestDeclaration
*) {}
758 void visit(PostBlitDeclaration
*) {}
759 void visit(DtorDeclaration
*) {}
760 void visit(StaticCtorDeclaration
*) {}
761 void visit(StaticDtorDeclaration
*) {}
762 void visit(TypeInfoDeclaration
*) {}
764 void emit(Scope
*sc
, Dsymbol
*s
, const utf8_t
*com
)
766 if (s
&& sc
->lastdc
&& isDitto(com
))
768 sc
->lastdc
->a
.push(s
);
772 // Put previous doc comment if exists
773 if (DocComment
*dc
= sc
->lastdc
)
775 // Put the declaration signatures as the document 'title'
776 buf
->writestring(ddoc_decl_s
);
777 for (size_t i
= 0; i
< dc
->a
.length
; i
++)
779 Dsymbol
*sx
= dc
->a
[i
];
783 size_t o
= buf
->offset
;
784 toDocBuffer(sx
, buf
, sc
);
785 highlightCode(sc
, sx
, buf
, o
);
789 buf
->writestring("$(DDOC_DITTO ");
791 size_t o
= buf
->offset
;
792 toDocBuffer(sx
, buf
, sc
);
793 highlightCode(sc
, sx
, buf
, o
);
797 buf
->writestring(ddoc_decl_e
);
799 // Put the ddoc comment as the document 'description'
800 buf
->writestring(ddoc_decl_dd_s
);
802 dc
->writeSections(sc
, &dc
->a
, buf
);
803 if (ScopeDsymbol
*sds
= dc
->a
[0]->isScopeDsymbol())
804 emitMemberComments(sds
, buf
, sc
);
806 buf
->writestring(ddoc_decl_dd_e
);
807 //printf("buf.2 = [[%.*s]]\n", buf->offset - o0, buf->data + o0);
812 DocComment
*dc
= DocComment::parse(s
, com
);
813 dc
->pmacrotable
= &sc
->_module
->macrotable
;
818 void visit(Declaration
*d
)
820 //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d->toChars(), d->comment);
821 //printf("type = %p\n", d->type);
822 const utf8_t
*com
= d
->comment
;
823 if (TemplateDeclaration
*td
= getEponymousParent(d
))
825 if (isDitto(td
->comment
))
828 com
= Lexer::combineComments(td
->comment
, com
);
834 if (!d
->type
&& !d
->isCtorDeclaration() && !d
->isAliasDeclaration())
836 if (d
->protection
.kind
== PROTprivate
|| sc
->protection
.kind
== PROTprivate
)
845 void visit(AggregateDeclaration
*ad
)
847 //printf("AggregateDeclaration::emitComment() '%s'\n", ad->toChars());
848 const utf8_t
*com
= ad
->comment
;
849 if (TemplateDeclaration
*td
= getEponymousParent(ad
))
851 if (isDitto(td
->comment
))
854 com
= Lexer::combineComments(td
->comment
, com
);
858 if (ad
->prot().kind
== PROTprivate
|| sc
->protection
.kind
== PROTprivate
)
869 void visit(TemplateDeclaration
*td
)
871 //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td->toChars(), td->kind());
872 if (td
->prot().kind
== PROTprivate
|| sc
->protection
.kind
== PROTprivate
)
877 if (Dsymbol
*ss
= getEponymousMember(td
))
882 emit(sc
, td
, td
->comment
);
885 void visit(EnumDeclaration
*ed
)
887 if (ed
->prot().kind
== PROTprivate
|| sc
->protection
.kind
== PROTprivate
)
889 if (ed
->isAnonymous() && ed
->members
)
891 for (size_t i
= 0; i
< ed
->members
->length
; i
++)
893 Dsymbol
*s
= (*ed
->members
)[i
];
894 emitComment(s
, buf
, sc
);
900 if (ed
->isAnonymous())
903 emit(sc
, ed
, ed
->comment
);
906 void visit(EnumMember
*em
)
908 //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em->toChars(), em->comment);
909 if (em
->prot().kind
== PROTprivate
|| sc
->protection
.kind
== PROTprivate
)
914 emit(sc
, em
, em
->comment
);
917 void visit(AttribDeclaration
*ad
)
919 //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
921 /* A general problem with this, illustrated by BUGZILLA 2516,
922 * is that attributes are not transmitted through to the underlying
923 * member declarations for template bodies, because semantic analysis
924 * is not done for template declaration bodies
925 * (only template instantiations).
926 * Hence, Ddoc omits attributes from template members.
929 Dsymbols
*d
= ad
->include(NULL
, NULL
);
933 for (size_t i
= 0; i
< d
->length
; i
++)
935 Dsymbol
*s
= (*d
)[i
];
936 //printf("AttribDeclaration::emitComment %s\n", s->toChars());
937 emitComment(s
, buf
, sc
);
942 void visit(ProtDeclaration
*pd
)
948 sc
->protection
= pd
->protection
;
949 visit((AttribDeclaration
*)pd
);
950 scx
->lastdc
= sc
->lastdc
;
955 void visit(ConditionalDeclaration
*cd
)
957 //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
958 if (cd
->condition
->inc
)
960 visit((AttribDeclaration
*)cd
);
964 /* If generating doc comment, be careful because if we're inside
965 * a template, then include(NULL, NULL) will fail.
967 Dsymbols
*d
= cd
->decl
? cd
->decl
: cd
->elsedecl
;
968 for (size_t i
= 0; i
< d
->length
; i
++)
970 Dsymbol
*s
= (*d
)[i
];
971 emitComment(s
, buf
, sc
);
976 EmitComment
v(buf
, sc
);
979 v
.emit(sc
, NULL
, NULL
);
984 /******************************* toDocBuffer **********************************/
986 void toDocBuffer(Dsymbol
*s
, OutBuffer
*buf
, Scope
*sc
)
988 class ToDocBuffer
: public Visitor
994 ToDocBuffer(OutBuffer
*buf
, Scope
*sc
)
999 void visit(Dsymbol
*s
)
1001 //printf("Dsymbol::toDocbuffer() %s\n", s->toChars());
1004 ::toCBuffer(s
, buf
, &hgs
);
1007 void prefix(Dsymbol
*s
)
1009 if (s
->isDeprecated())
1010 buf
->writestring("deprecated ");
1012 if (Declaration
*d
= s
->isDeclaration())
1014 emitProtection(buf
, d
->protection
);
1017 buf
->writestring("static ");
1018 else if (d
->isFinal())
1019 buf
->writestring("final ");
1020 else if (d
->isAbstract())
1021 buf
->writestring("abstract ");
1023 if (!d
->isFuncDeclaration()) // functionToBufferFull handles this
1026 buf
->writestring("const ");
1027 if (d
->isImmutable())
1028 buf
->writestring("immutable ");
1029 if (d
->isSynchronized())
1030 buf
->writestring("synchronized ");
1032 if (d
->storage_class
& STCmanifest
)
1033 buf
->writestring("enum ");
1038 void visit(Declaration
*d
)
1043 TemplateDeclaration
*td
= getEponymousParent(d
);
1044 //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d->toChars(), d->originalType ? d->originalType->toChars() : "--", td ? td->toChars() : "--");
1049 if (d
->isDeprecated())
1050 buf
->writestring("$(DEPRECATED ");
1056 Type
*origType
= d
->originalType
? d
->originalType
: d
->type
;
1057 if (origType
->ty
== Tfunction
)
1059 functionToBufferFull((TypeFunction
*)origType
, buf
, d
->ident
, &hgs
, td
);
1062 ::toCBuffer(origType
, buf
, d
->ident
, &hgs
);
1065 buf
->writestring(d
->ident
->toChars());
1067 if (d
->isVarDeclaration() && td
)
1069 buf
->writeByte('(');
1070 if (td
->origParameters
&& td
->origParameters
->length
)
1072 for (size_t i
= 0; i
< td
->origParameters
->length
; i
++)
1075 buf
->writestring(", ");
1076 toCBuffer((*td
->origParameters
)[i
], buf
, &hgs
);
1079 buf
->writeByte(')');
1082 // emit constraints if declaration is a templated declaration
1083 if (td
&& td
->constraint
)
1085 buf
->writestring(" if (");
1086 ::toCBuffer(td
->constraint
, buf
, &hgs
);
1087 buf
->writeByte(')');
1090 if (d
->isDeprecated())
1091 buf
->writestring(")");
1093 buf
->writestring(";\n");
1096 void visit(AliasDeclaration
*ad
)
1098 //printf("AliasDeclaration::toDocbuffer() %s\n", ad->toChars());
1102 if (ad
->isDeprecated())
1103 buf
->writestring("deprecated ");
1105 emitProtection(buf
, ad
->protection
);
1106 buf
->printf("alias %s = ", ad
->toChars());
1108 if (Dsymbol
*s
= ad
->aliassym
) // ident alias
1110 prettyPrintDsymbol(s
, ad
->parent
);
1112 else if (Type
*type
= ad
->getType()) // type alias
1114 if (type
->ty
== Tclass
|| type
->ty
== Tstruct
|| type
->ty
== Tenum
)
1116 if (Dsymbol
*s
= type
->toDsymbol(NULL
)) // elaborate type
1117 prettyPrintDsymbol(s
, ad
->parent
);
1119 buf
->writestring(type
->toChars());
1124 buf
->writestring(type
->toChars());
1128 buf
->writestring(";\n");
1131 void parentToBuffer(Dsymbol
*s
)
1133 if (s
&& !s
->isPackage() && !s
->isModule())
1135 parentToBuffer(s
->parent
);
1136 buf
->writestring(s
->toChars());
1137 buf
->writestring(".");
1141 static bool inSameModule(Dsymbol
*s
, Dsymbol
*p
)
1143 for ( ; s
; s
= s
->parent
)
1149 for ( ; p
; p
= p
->parent
)
1158 void prettyPrintDsymbol(Dsymbol
*s
, Dsymbol
*parent
)
1160 if (s
->parent
&& (s
->parent
== parent
)) // in current scope -> naked name
1162 buf
->writestring(s
->toChars());
1164 else if (!inSameModule(s
, parent
)) // in another module -> full name
1166 buf
->writestring(s
->toPrettyChars());
1168 else // nested in a type in this module -> full name w/o module name
1170 // if alias is nested in a user-type use module-scope lookup
1171 if (!parent
->isModule() && !parent
->isPackage())
1172 buf
->writestring(".");
1174 parentToBuffer(s
->parent
);
1175 buf
->writestring(s
->toChars());
1179 void visit(AggregateDeclaration
*ad
)
1184 buf
->printf("%s %s", ad
->kind(), ad
->toChars());
1185 buf
->writestring(";\n");
1188 void visit(StructDeclaration
*sd
)
1190 //printf("StructDeclaration::toDocbuffer() %s\n", sd->toChars());
1194 if (TemplateDeclaration
*td
= getEponymousParent(sd
))
1196 toDocBuffer(td
, buf
, sc
);
1200 buf
->printf("%s %s", sd
->kind(), sd
->toChars());
1202 buf
->writestring(";\n");
1205 void visit(ClassDeclaration
*cd
)
1207 //printf("ClassDeclaration::toDocbuffer() %s\n", cd->toChars());
1211 if (TemplateDeclaration
*td
= getEponymousParent(cd
))
1213 toDocBuffer(td
, buf
, sc
);
1217 if (!cd
->isInterfaceDeclaration() && cd
->isAbstract())
1218 buf
->writestring("abstract ");
1219 buf
->printf("%s %s", cd
->kind(), cd
->toChars());
1222 for (size_t i
= 0; i
< cd
->baseclasses
->length
; i
++)
1224 BaseClass
*bc
= (*cd
->baseclasses
)[i
];
1226 if (bc
->sym
&& bc
->sym
->ident
== Id::Object
)
1230 buf
->writestring(", ");
1233 buf
->writestring(": ");
1236 emitProtection(buf
, Prot(PROTpublic
));
1239 buf
->printf("$(DDOC_PSUPER_SYMBOL %s)", bc
->sym
->toPrettyChars());
1244 ::toCBuffer(bc
->type
, buf
, NULL
, &hgs
);
1247 buf
->writestring(";\n");
1250 void visit(EnumDeclaration
*ed
)
1255 buf
->printf("%s %s", ed
->kind(), ed
->toChars());
1258 buf
->writestring(": $(DDOC_ENUM_BASETYPE ");
1260 ::toCBuffer(ed
->memtype
, buf
, NULL
, &hgs
);
1261 buf
->writestring(")");
1263 buf
->writestring(";\n");
1266 void visit(EnumMember
*em
)
1271 buf
->writestring(em
->toChars());
1275 ToDocBuffer
v(buf
, sc
);
1279 /********************************* DocComment *********************************/
1281 DocComment
*DocComment::parse(Dsymbol
*s
, const utf8_t
*comment
)
1283 //printf("parse(%s): '%s'\n", s->toChars(), comment);
1284 DocComment
*dc
= new DocComment();
1289 dc
->parseSections(comment
);
1291 for (size_t i
= 0; i
< dc
->sections
.length
; i
++)
1293 Section
*sec
= dc
->sections
[i
];
1295 if (icmp("copyright", sec
->name
, sec
->namelen
) == 0)
1297 dc
->copyright
= sec
;
1299 if (icmp("macros", sec
->name
, sec
->namelen
) == 0)
1308 /*****************************************
1309 * Parse next paragraph out of *pcomment.
1310 * Update *pcomment to point past paragraph.
1311 * Returns NULL if no more paragraphs.
1312 * If paragraph ends in 'identifier:',
1313 * then (*pcomment)[0 .. idlen] is the identifier.
1316 void DocComment::parseSections(const utf8_t
*comment
)
1319 const utf8_t
*pstart
;
1321 const utf8_t
*idstart
= NULL
; // dead-store to prevent spurious warning
1324 const utf8_t
*name
= NULL
;
1327 //printf("parseSections('%s')\n", comment);
1331 const utf8_t
*pstart0
= p
;
1332 p
= skipwhitespace(p
);
1336 /* Find end of section, which is ended by one of:
1337 * 'identifier:' (but not inside a code section)
1344 // Check for start/end of a code section
1349 // restore leading indentation
1350 while (pstart0
< pstart
&& isIndentWS(pstart
-1)) --pstart
;
1359 // BUG: handle UTF PS and LS too
1360 if ((!*p
|| *p
== '\r' || *p
== '\n') && numdash
>= 3)
1365 if (!inCode
&& isIdStart(p
))
1367 const utf8_t
*q
= p
+ utfStride(p
);
1370 // Detected tag ends it
1371 if (*q
== ':' && isupper(*p
)
1372 && (isspace(q
[1]) || q
[1] == 0))
1376 for (pend
= p
; pend
> pstart
; pend
--)
1378 if (pend
[-1] == '\n')
1392 if (*p
== '\n' && !summary
&& !namelen
&& !inCode
)
1403 p
= skipwhitespace(p
);
1407 if (namelen
|| pstart
< pend
)
1410 if (icmp("Params", name
, namelen
) == 0)
1411 s
= new ParamSection();
1412 else if (icmp("Macros", name
, namelen
) == 0)
1413 s
= new MacroSection();
1417 s
->namelen
= namelen
;
1419 s
->bodylen
= pend
- pstart
;
1422 //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
1426 if (!summary
&& !namelen
)
1445 void DocComment::writeSections(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
)
1449 //printf("DocComment::writeSections()\n");
1450 Loc loc
= (*a
)[0]->loc
;
1451 if (Module
*m
= (*a
)[0]->isModule())
1457 size_t offset1
= buf
->offset
;
1458 buf
->writestring("$(DDOC_SECTIONS ");
1459 size_t offset2
= buf
->offset
;
1461 for (size_t i
= 0; i
< sections
.length
; i
++)
1463 Section
*sec
= sections
[i
];
1467 //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
1468 if (!sec
->namelen
&& i
== 0)
1470 buf
->writestring("$(DDOC_SUMMARY ");
1471 size_t o
= buf
->offset
;
1472 buf
->write(sec
->body
, sec
->bodylen
);
1473 escapeStrayParenthesis(loc
, buf
, o
);
1474 highlightText(sc
, a
, buf
, o
);
1475 buf
->writestring(")\n");
1478 sec
->write(loc
, this, sc
, a
, buf
);
1481 for (size_t i
= 0; i
< a
->length
; i
++)
1483 Dsymbol
*s
= (*a
)[i
];
1484 if (Dsymbol
*td
= getEponymousParent(s
))
1487 for (UnitTestDeclaration
*utd
= s
->ddocUnittest
; utd
; utd
= utd
->ddocUnittest
)
1489 if (utd
->protection
.kind
== PROTprivate
|| !utd
->comment
|| !utd
->fbody
)
1492 // Strip whitespaces to avoid showing empty summary
1493 const utf8_t
*c
= utd
->comment
;
1494 while (*c
== ' ' || *c
== '\t' || *c
== '\n' || *c
== '\r') ++c
;
1496 buf
->writestring("$(DDOC_EXAMPLES ");
1498 size_t o
= buf
->offset
;
1499 buf
->writestring((const char *)c
);
1503 size_t n
= getCodeIndent(utd
->codedoc
);
1504 while (n
--) buf
->writeByte(' ');
1505 buf
->writestring("----\n");
1506 buf
->writestring(utd
->codedoc
);
1507 buf
->writestring("----\n");
1508 highlightText(sc
, a
, buf
, o
);
1511 buf
->writestring(")");
1515 if (buf
->offset
== offset2
)
1517 /* Didn't write out any sections, so back out last write
1519 buf
->offset
= offset1
;
1520 buf
->writestring("$(DDOC_BLANKLINE)\n");
1523 buf
->writestring(")\n");
1526 /***************************************************
1529 void Section::write(Loc loc
, DocComment
*, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
)
1535 static const char *table
[] =
1537 "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
1538 "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
1539 "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
1543 for (size_t i
= 0; table
[i
]; i
++)
1545 if (icmp(table
[i
], name
, namelen
) == 0)
1547 buf
->printf("$(DDOC_%s ", table
[i
]);
1552 buf
->writestring("$(DDOC_SECTION ");
1554 // Replace _ characters with spaces
1555 buf
->writestring("$(DDOC_SECTION_H ");
1556 size_t o
= buf
->offset
;
1557 for (size_t u
= 0; u
< namelen
; u
++)
1560 buf
->writeByte((c
== '_') ? ' ' : c
);
1562 escapeStrayParenthesis(loc
, buf
, o
);
1563 buf
->writestring(":)\n");
1567 buf
->writestring("$(DDOC_DESCRIPTION ");
1570 size_t o
= buf
->offset
;
1571 buf
->write(body
, bodylen
);
1572 escapeStrayParenthesis(loc
, buf
, o
);
1573 highlightText(sc
, a
, buf
, o
);
1574 buf
->writestring(")\n");
1577 /***************************************************
1580 void ParamSection::write(Loc loc
, DocComment
*, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
)
1583 Dsymbol
*s
= (*a
)[0]; // test
1585 const utf8_t
*p
= body
;
1586 size_t len
= bodylen
;
1587 const utf8_t
*pend
= p
+ len
;
1589 const utf8_t
*tempstart
= NULL
;
1592 const utf8_t
*namestart
= NULL
;
1593 size_t namelen
= 0; // !=0 if line continuation
1595 const utf8_t
*textstart
= NULL
;
1598 size_t paramcount
= 0;
1600 buf
->writestring("$(DDOC_PARAMS ");
1603 // Skip to start of macro
1618 if (isIdStart(p
) || isCVariadicArg(p
, pend
- p
))
1621 goto Ltext
; // continuation of prev macro
1630 if (isCVariadicArg(p
, pend
- p
))
1633 templen
= p
- tempstart
;
1635 while (*p
== ' ' || *p
== '\t')
1641 goto Ltext
; // continuation of prev macro
1648 // Output existing param
1651 //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1654 buf
->writestring("$(DDOC_PARAM_ROW ");
1656 buf
->writestring("$(DDOC_PARAM_ID ");
1658 size_t o
= buf
->offset
;
1659 Parameter
*fparam
= isFunctionParameter(a
, namestart
, namelen
);
1662 // Comments on a template might refer to function parameters within.
1663 // Search the parameters of nested eponymous functions (with the same name.)
1664 fparam
= isEponymousFunctionParameter(a
, namestart
, namelen
);
1666 bool isCVariadic
= isCVariadicParameter(a
, namestart
, namelen
);
1669 buf
->writestring("...");
1671 else if (fparam
&& fparam
->type
&& fparam
->ident
)
1673 ::toCBuffer(fparam
->type
, buf
, fparam
->ident
, &hgs
);
1677 if (isTemplateParameter(a
, namestart
, namelen
))
1679 // 10236: Don't count template parameters for params check
1684 warning(s
->loc
, "Ddoc: function declaration has no parameter '%.*s'", (int)namelen
, namestart
);
1686 buf
->write(namestart
, namelen
);
1688 escapeStrayParenthesis(loc
, buf
, o
);
1689 highlightCode(sc
, a
, buf
, o
);
1691 buf
->writestring(")\n");
1693 buf
->writestring("$(DDOC_PARAM_DESC ");
1695 size_t o
= buf
->offset
;
1696 buf
->write(textstart
, textlen
);
1697 escapeStrayParenthesis(loc
, buf
, o
);
1698 highlightText(sc
, a
, buf
, o
);
1700 buf
->writestring(")");
1702 buf
->writestring(")\n");
1708 namestart
= tempstart
;
1711 while (*p
== ' ' || *p
== '\t')
1718 textlen
= p
- textstart
;
1726 while (*p
++ != '\n')
1730 goto L1
; // write out last one
1731 buf
->writestring(")\n");
1733 TypeFunction
*tf
= a
->length
== 1 ? isTypeFunction(s
) : NULL
;
1736 size_t pcount
= (tf
->parameters
? tf
->parameters
->length
: 0) + (int)(tf
->varargs
== 1);
1737 if (pcount
!= paramcount
)
1739 warning(s
->loc
, "Ddoc: parameter count mismatch");
1744 /***************************************************
1747 void MacroSection::write(Loc
, DocComment
*dc
, Scope
*, Dsymbols
*, OutBuffer
*)
1749 //printf("MacroSection::write()\n");
1750 DocComment::parseMacros(dc
->pescapetable
, dc
->pmacrotable
, body
, bodylen
);
1753 /************************************************
1754 * Parse macros out of Macros: section.
1755 * Macros are of the form:
1761 void DocComment::parseMacros(Escape
**pescapetable
, Macro
**pmacrotable
, const utf8_t
*m
, size_t mlen
)
1763 const utf8_t
*p
= m
;
1765 const utf8_t
*pend
= p
+ len
;
1767 const utf8_t
*tempstart
= NULL
;
1770 const utf8_t
*namestart
= NULL
;
1771 size_t namelen
= 0; // !=0 if line continuation
1773 const utf8_t
*textstart
= NULL
;
1778 // Skip to start of macro
1799 goto Ltext
; // continuation of prev macro
1814 templen
= p
- tempstart
;
1820 if (!(*p
== ' ' || *p
== '\t'))
1828 goto Ltext
; // continuation of prev macro
1837 // Output existing macro
1839 //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1840 if (icmp("ESCAPES", namestart
, namelen
) == 0)
1841 parseEscapes(pescapetable
, textstart
, textlen
);
1843 Macro::define(pmacrotable
, namestart
, namelen
, textstart
, textlen
);
1849 namestart
= tempstart
;
1852 while (p
< pend
&& (*p
== ' ' || *p
== '\t'))
1857 while (p
< pend
&& *p
!= '\r' && *p
!= '\n')
1859 textlen
= p
- textstart
;
1862 //printf("p = %p, pend = %p\n", p, pend);
1869 while (p
< pend
&& *p
!= '\r' && *p
!= '\n')
1874 goto L1
; // write out last one
1877 /**************************************
1878 * Parse escapes of the form:
1880 * where c is a single character.
1881 * Multiple escapes can be separated
1882 * by whitespace and/or commas.
1885 void DocComment::parseEscapes(Escape
**pescapetable
, const utf8_t
*textstart
, size_t textlen
)
1887 Escape
*escapetable
= *pescapetable
;
1891 escapetable
= new Escape
;
1892 memset(escapetable
, 0, sizeof(Escape
));
1893 *pescapetable
= escapetable
;
1895 //printf("parseEscapes('%.*s') pescapetable = %p\n", textlen, textstart, pescapetable);
1896 const utf8_t
*p
= textstart
;
1897 const utf8_t
*pend
= p
+ textlen
;
1905 if (!(*p
== ' ' || *p
== '\t' || *p
== '\r' || *p
== '\n' || *p
== ','))
1909 if (p
[0] != '/' || p
[2] != '/')
1913 const utf8_t
*start
= p
;
1922 size_t len
= p
- start
;
1923 char *s
= (char *)memcpy(mem
.xmalloc(len
+ 1), start
, len
);
1925 escapetable
->strings
[c
] = s
;
1926 //printf("\t%c = '%s'\n", c, s);
1932 /******************************************
1933 * Compare 0-terminated string with length terminated string.
1934 * Return < 0, ==0, > 0
1937 int cmp(const char *stringz
, const void *s
, size_t slen
)
1939 size_t len1
= strlen(stringz
);
1942 return (int)(len1
- slen
);
1943 return memcmp(stringz
, s
, slen
);
1946 int icmp(const char *stringz
, const void *s
, size_t slen
)
1948 size_t len1
= strlen(stringz
);
1951 return (int)(len1
- slen
);
1952 return Port::memicmp(stringz
, (const char *)s
, slen
);
1955 /*****************************************
1956 * Return true if comment consists entirely of "ditto".
1959 bool isDitto(const utf8_t
*comment
)
1963 const utf8_t
*p
= skipwhitespace(comment
);
1965 if (Port::memicmp((const char *)p
, "ditto", 5) == 0 && *skipwhitespace(p
+ 5) == 0)
1971 /**********************************************
1975 const utf8_t
*skipwhitespace(const utf8_t
*p
)
1992 /************************************************
1993 * Scan forward to one of:
1994 * start of identifier
1995 * beginning of next line
1999 size_t skiptoident(OutBuffer
*buf
, size_t i
)
2001 while (i
< buf
->offset
)
2006 if (utf_decodeChar((utf8_t
*)buf
->data
, buf
->offset
, &i
, &c
))
2008 /* Ignore UTF errors, but still consume input
2017 else if (!(isalpha(c
) || c
== '_' || c
== '\n'))
2025 /************************************************
2026 * Scan forward past end of identifier.
2029 size_t skippastident(OutBuffer
*buf
, size_t i
)
2031 while (i
< buf
->offset
)
2036 if (utf_decodeChar((utf8_t
*)buf
->data
, buf
->offset
, &i
, &c
))
2038 /* Ignore UTF errors, but still consume input
2047 else if (isalnum(c
) || c
== '_')
2056 /************************************************
2057 * Scan forward past URL starting at i.
2058 * We don't want to highlight parts of a URL.
2061 * index just past it if it is a URL
2064 size_t skippastURL(OutBuffer
*buf
, size_t i
)
2066 size_t length
= buf
->offset
- i
;
2067 utf8_t
*p
= (utf8_t
*)&buf
->data
[i
];
2069 unsigned sawdot
= 0;
2071 if (length
> 7 && Port::memicmp((char *)p
, "http://", 7) == 0)
2075 else if (length
> 8 && Port::memicmp((char *)p
, "https://", 8) == 0)
2082 for (; j
< length
; j
++)
2087 if (c
== '-' || c
== '_' || c
== '?' ||
2088 c
== '=' || c
== '%' || c
== '&' ||
2089 c
== '/' || c
== '+' || c
== '#' ||
2107 /****************************************************
2110 bool isIdentifier(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
2112 for (size_t i
= 0; i
< a
->length
; i
++)
2114 const char *s
= (*a
)[i
]->ident
->toChars();
2115 if (cmp(s
, p
, len
) == 0)
2121 /****************************************************
2124 bool isKeyword(utf8_t
*p
, size_t len
)
2126 static const char *table
[] = { "true", "false", "null", NULL
};
2128 for (int i
= 0; table
[i
]; i
++)
2130 if (cmp(table
[i
], p
, len
) == 0)
2136 /****************************************************
2139 TypeFunction
*isTypeFunction(Dsymbol
*s
)
2141 FuncDeclaration
*f
= s
->isFuncDeclaration();
2143 /* f->type may be NULL for template members.
2147 Type
*t
= f
->originalType
? f
->originalType
: f
->type
;
2148 if (t
->ty
== Tfunction
)
2149 return (TypeFunction
*)t
;
2154 /****************************************************
2157 Parameter
*isFunctionParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
2159 for (size_t i
= 0; i
< a
->length
; i
++)
2161 Parameter
*fparam
= isFunctionParameter((*a
)[i
], p
, len
);
2170 /****************************************************
2173 TemplateParameter
*isTemplateParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
2175 for (size_t i
= 0; i
< a
->length
; i
++)
2177 TemplateDeclaration
*td
= (*a
)[i
]->isTemplateDeclaration();
2178 // Check for the parent, if the current symbol is not a template declaration.
2180 td
= getEponymousParent((*a
)[i
]);
2181 if (td
&& td
->origParameters
)
2183 for (size_t k
= 0; k
< td
->origParameters
->length
; k
++)
2185 TemplateParameter
*tp
= (*td
->origParameters
)[k
];
2186 if (tp
->ident
&& cmp(tp
->ident
->toChars(), p
, len
) == 0)
2196 /****************************************************
2197 * Return true if str is a reserved symbol name
2198 * that starts with a double underscore.
2201 bool isReservedName(utf8_t
*str
, size_t len
)
2203 static const char *table
[] = {
2204 "__ctor", "__dtor", "__postblit", "__invariant", "__unitTest",
2205 "__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result",
2206 "__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp",
2207 "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
2208 "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
2209 "__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", NULL
};
2211 for (int i
= 0; table
[i
]; i
++)
2213 if (cmp(table
[i
], str
, len
) == 0)
2219 /**************************************************
2220 * Highlight text section.
2223 void highlightText(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
)
2225 Dsymbol
*s
= a
->length
? (*a
)[0] : NULL
; // test
2227 //printf("highlightText()\n");
2229 int leadingBlank
= 1;
2232 //int inComment = 0; // in <!-- ... --> comment
2233 size_t iCodeStart
= 0; // start of code section
2234 size_t codeIndent
= 0;
2236 size_t iLineStart
= offset
;
2238 for (size_t i
= offset
; i
< buf
->offset
; i
++)
2240 utf8_t c
= buf
->data
[i
];
2252 // `inline code` is only valid if contained on a single line
2253 // otherwise, the backticks should be output literally.
2255 // This lets things like `output from the linker' display
2256 // unmolested while keeping the feature consistent with GitHub.
2259 inCode
= false; // the backtick also assumes we're in code
2261 // Nothing else is necessary since the DDOC_BACKQUOTED macro is
2262 // inserted lazily at the close quote, meaning the rest of the
2263 // text is already OK.
2266 if (!sc
->_module
->isDocFile
&&
2267 !inCode
&& i
== iLineStart
&& i
+ 1 < buf
->offset
) // if "\n\n"
2269 static const char blankline
[] = "$(DDOC_BLANKLINE)\n";
2271 i
= buf
->insert(i
, blankline
, strlen(blankline
));
2282 utf8_t
*p
= (utf8_t
*)&buf
->data
[i
];
2283 const char *se
= sc
->_module
->escapetable
->escapeChar('<');
2284 if (se
&& strcmp(se
, "<") == 0)
2287 // Skip over comments
2288 if (p
[1] == '!' && p
[2] == '-' && p
[3] == '-')
2294 if (j
== buf
->offset
)
2296 if (p
[0] == '-' && p
[1] == '-' && p
[2] == '>')
2298 i
= j
+ 2; // place on closing '>'
2307 // Skip over HTML tag
2308 if (isalpha(p
[1]) || (p
[1] == '/' && isalpha(p
[2])))
2314 if (j
== buf
->offset
)
2318 i
= j
; // place on closing '>'
2328 // Replace '<' with '<' character entity
2331 size_t len
= strlen(se
);
2333 i
= buf
->insert(i
, se
, len
);
2334 i
--; // point to ';'
2343 // Replace '>' with '>' character entity
2344 const char *se
= sc
->_module
->escapetable
->escapeChar('>');
2347 size_t len
= strlen(se
);
2349 i
= buf
->insert(i
, se
, len
);
2350 i
--; // point to ';'
2359 utf8_t
*p
= (utf8_t
*)&buf
->data
[i
];
2360 if (p
[1] == '#' || isalpha(p
[1]))
2361 break; // already a character entity
2362 // Replace '&' with '&' character entity
2363 const char *se
= sc
->_module
->escapetable
->escapeChar('&');
2366 size_t len
= strlen(se
);
2368 i
= buf
->insert(i
, se
, len
);
2369 i
--; // point to ';'
2382 codebuf
.write(buf
->data
+ iCodeStart
+ 1, i
- (iCodeStart
+ 1));
2384 // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
2385 highlightCode(sc
, a
, &codebuf
, 0);
2387 buf
->remove(iCodeStart
, i
- iCodeStart
+ 1); // also trimming off the current `
2389 static const char pre
[] = "$(DDOC_BACKQUOTED ";
2390 i
= buf
->insert(iCodeStart
, pre
, strlen(pre
));
2391 i
= buf
->insert(i
, (char *)codebuf
.data
, codebuf
.offset
);
2392 i
= buf
->insert(i
, ")", 1);
2394 i
--; // point to the ending ) so when the for loop does i++, it will see the next character
2404 codeIndent
= 0; // inline code is not indented
2406 // All we do here is set the code flags and record
2407 // the location. The macro will be inserted lazily
2408 // so we can easily cancel the inBacktick if we come
2409 // across a newline character.
2415 /* A line beginning with --- delimits a code section.
2416 * inCode tells us if it is start or end of a code section.
2427 if (i
>= buf
->offset
)
2438 if (i
+ 1 >= buf
->offset
)
2440 if (buf
->data
[i
+ 1] == '\n')
2446 // BUG: handle UTF PS and LS too
2453 // We have the start/end of a code section
2455 // Remove the entire --- line, including blanks and \n
2456 buf
->remove(iLineStart
, i
- iLineStart
+ eollen
);
2459 if (inCode
&& (i
<= iCodeStart
))
2461 // Empty code section, just remove it completely.
2469 // The code section is from iCodeStart to i
2472 codebuf
.write(buf
->data
+ iCodeStart
, i
- iCodeStart
);
2473 codebuf
.writeByte(0);
2475 // Remove leading indentations from all lines
2476 bool lineStart
= true;
2477 utf8_t
*endp
= (utf8_t
*)codebuf
.data
+ codebuf
.offset
;
2478 for (utf8_t
*p
= (utf8_t
*)codebuf
.data
; p
< endp
; )
2482 size_t j
= codeIndent
;
2484 while (j
-- > 0 && q
< endp
&& isIndentWS(q
))
2486 codebuf
.remove(p
- (utf8_t
*)codebuf
.data
, q
- p
);
2487 assert((utf8_t
*)codebuf
.data
<= p
);
2488 assert(p
< (utf8_t
*)codebuf
.data
+ codebuf
.offset
);
2490 endp
= (utf8_t
*)codebuf
.data
+ codebuf
.offset
; // update
2498 highlightCode2(sc
, a
, &codebuf
, 0);
2499 buf
->remove(iCodeStart
, i
- iCodeStart
);
2500 i
= buf
->insert(iCodeStart
, codebuf
.data
, codebuf
.offset
);
2501 i
= buf
->insert(i
, (const char *)")\n", 2);
2502 i
-= 2; // in next loop, c should be '\n'
2506 static const char d_code
[] = "$(D_CODE ";
2509 codeIndent
= istart
- iLineStart
; // save indent count
2510 i
= buf
->insert(i
, d_code
, strlen(d_code
));
2512 i
--; // place i on >
2513 leadingBlank
= true;
2520 if (sc
->_module
->isDocFile
|| inCode
)
2523 utf8_t
*start
= (utf8_t
*)buf
->data
+ i
;
2524 if (isIdStart(start
))
2526 size_t j
= skippastident(buf
, i
);
2529 size_t k
= skippastURL(buf
, i
);
2540 // leading '_' means no highlight unless it's a reserved symbol name
2542 (i
== 0 || !isdigit(*(start
- 1))) &&
2543 (i
== buf
->offset
- 1 || !isReservedName(start
, len
)))
2549 if (isIdentifier(a
, start
, len
))
2551 i
= buf
->bracket(i
, "$(DDOC_PSYMBOL ", j
, ")") - 1;
2554 if (isKeyword(start
, len
))
2556 i
= buf
->bracket(i
, "$(DDOC_KEYWORD ", j
, ")") - 1;
2559 if (isFunctionParameter(a
, start
, len
))
2561 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2562 i
= buf
->bracket(i
, "$(DDOC_PARAM ", j
, ")") - 1;
2572 error(s
? s
->loc
: Loc(), "unmatched --- in DDoc comment");
2575 /**************************************************
2576 * Highlight code for DDOC section.
2579 void highlightCode(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, size_t offset
)
2581 //printf("highlightCode(s = %s '%s')\n", s->kind(), s->toChars());
2583 emitAnchor(&ancbuf
, s
, sc
);
2584 buf
->insert(offset
, (char *)ancbuf
.data
, ancbuf
.offset
);
2585 offset
+= ancbuf
.offset
;
2589 highlightCode(sc
, &a
, buf
, offset
);
2592 /****************************************************
2595 void highlightCode(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
)
2597 //printf("highlightCode(a = '%s')\n", a->toChars());
2599 for (size_t i
= offset
; i
< buf
->offset
; i
++)
2601 utf8_t c
= buf
->data
[i
];
2602 const char *se
= sc
->_module
->escapetable
->escapeChar(c
);
2605 size_t len
= strlen(se
);
2607 i
= buf
->insert(i
, se
, len
);
2608 i
--; // point to ';'
2612 utf8_t
*start
= (utf8_t
*)buf
->data
+ i
;
2613 if (isIdStart(start
))
2615 size_t j
= skippastident(buf
, i
);
2619 if (isIdentifier(a
, start
, len
))
2621 i
= buf
->bracket(i
, "$(DDOC_PSYMBOL ", j
, ")") - 1;
2624 if (isFunctionParameter(a
, start
, len
))
2626 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2627 i
= buf
->bracket(i
, "$(DDOC_PARAM ", j
, ")") - 1;
2636 /****************************************
2639 void highlightCode3(Scope
*sc
, OutBuffer
*buf
, const utf8_t
*p
, const utf8_t
*pend
)
2641 for (; p
< pend
; p
++)
2643 const char *s
= sc
->_module
->escapetable
->escapeChar(*p
);
2645 buf
->writestring(s
);
2651 /**************************************************
2652 * Highlight code for CODE section.
2655 void highlightCode2(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
)
2657 unsigned errorsave
= global
.errors
;
2658 Lexer
lex(NULL
, (utf8_t
*)buf
->data
, 0, buf
->offset
- 1, 0, 1);
2660 const utf8_t
*lastp
= (utf8_t
*)buf
->data
;
2662 //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data);
2663 res
.reserve(buf
->offset
);
2668 highlightCode3(sc
, &res
, lastp
, tok
.ptr
);
2670 const char *highlight
= NULL
;
2677 size_t len
= lex
.p
- tok
.ptr
;
2678 if (isIdentifier(a
, tok
.ptr
, len
))
2680 highlight
= "$(D_PSYMBOL ";
2683 if (isFunctionParameter(a
, tok
.ptr
, len
))
2685 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2686 highlight
= "$(D_PARAM ";
2692 highlight
= "$(D_COMMENT ";
2696 highlight
= "$(D_STRING ";
2700 if (tok
.isKeyword())
2701 highlight
= "$(D_KEYWORD ";
2706 res
.writestring(highlight
);
2707 size_t o
= res
.offset
;
2708 highlightCode3(sc
, &res
, tok
.ptr
, lex
.p
);
2709 if (tok
.value
== TOKcomment
|| tok
.value
== TOKstring
)
2710 escapeDdocString(&res
, o
); // Bugzilla 7656, 7715, and 10519
2714 highlightCode3(sc
, &res
, tok
.ptr
, lex
.p
);
2715 if (tok
.value
== TOKeof
)
2719 buf
->setsize(offset
);
2721 global
.errors
= errorsave
;
2724 /***************************************
2725 * Find character string to replace c with.
2728 const char *Escape::escapeChar(unsigned c
)
2731 //printf("escapeChar('%c') => %p, %p\n", c, strings, strings[c]);
2735 /****************************************
2736 * Determine if p points to the start of a "..." parameter identifier.
2739 bool isCVariadicArg(const utf8_t
*p
, size_t len
)
2741 return len
>= 3 && cmp("...", p
, 3) == 0;
2744 /****************************************
2745 * Determine if p points to the start of an identifier.
2748 bool isIdStart(const utf8_t
*p
)
2751 if (isalpha(c
) || c
== '_')
2756 if (utf_decodeChar(p
, 4, &i
, &c
))
2757 return false; // ignore errors
2764 /****************************************
2765 * Determine if p points to the rest of an identifier.
2768 bool isIdTail(const utf8_t
*p
)
2771 if (isalnum(c
) || c
== '_')
2776 if (utf_decodeChar(p
, 4, &i
, &c
))
2777 return false; // ignore errors
2784 /****************************************
2785 * Determine if p points to the indentation space.
2788 bool isIndentWS(const utf8_t
*p
)
2790 return (*p
== ' ') || (*p
== '\t');
2793 /*****************************************
2794 * Return number of bytes in UTF character.
2797 int utfStride(const utf8_t
*p
)
2803 utf_decodeChar(p
, 4, &i
, &c
); // ignore errors, but still consume input