]>
git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/doc.c
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2021 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
->parameterList
.varargs
== VARARGvariadic
&& 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
->parameterList
.parameters
)
143 for (size_t k
= 0; k
< tf
->parameterList
.parameters
->length
; k
++)
145 Parameter
*fparam
= (*tf
->parameterList
.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
.slice().ptr
, mbuf
.length());
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.length(), buf.slice().ptr);
431 Macro::define(&m
->macrotable
, (const utf8_t
*)"BODY", 4, (const utf8_t
*)buf
.slice().ptr
, buf
.length());
434 buf2
.writestring("$(DDOC)\n");
435 size_t end
= buf2
.length();
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
.length());
444 utf8_t
*p
= (utf8_t
*)buf2
.slice().ptr
;
445 for (size_t j
= 0; j
< buf2
.length(); j
++)
448 if (c
== 0xFF && j
+ 1 < buf2
.length())
457 buf
.writestring("\r\n");
458 if (j
+ 1 < buf2
.length() && p
[j
+ 1] == '\n')
468 // Transfer image to file
470 m
->docfile
->setbuffer(buf
.slice().ptr
, buf
.length());
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
->length(); u
++)
487 utf8_t c
= buf
->slice().ptr
[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
->length(); u
++)
523 utf8_t c
= buf
->slice().ptr
[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
->length(); u
> start
;)
552 utf8_t c
= buf
->slice().ptr
[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
.peekChars());
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
)
667 dsymbolSemantic(tm
, sc
);
668 TemplateDeclaration
*td
= (tm
&& tm
->tempdecl
) ?
669 tm
->tempdecl
->isTemplateDeclaration() : NULL
;
670 if (td
&& td
->members
)
672 for (size_t i
= 0; i
< td
->members
->length
; i
++)
674 Dsymbol
*sm
= (*td
->members
)[i
];
675 TemplateMixin
*tmc
= sm
->isTemplateMixin();
676 if (tmc
&& tmc
->comment
)
677 expandTemplateMixinComments(tmc
, buf
, sc
);
679 emitComment(sm
, buf
, sc
);
684 void emitMemberComments(ScopeDsymbol
*sds
, OutBuffer
*buf
, Scope
*sc
)
689 //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
691 const char *m
= "$(DDOC_MEMBERS ";
692 if (sds
->isTemplateDeclaration())
693 m
= "$(DDOC_TEMPLATE_MEMBERS ";
694 else if (sds
->isClassDeclaration())
695 m
= "$(DDOC_CLASS_MEMBERS ";
696 else if (sds
->isStructDeclaration())
697 m
= "$(DDOC_STRUCT_MEMBERS ";
698 else if (sds
->isEnumDeclaration())
699 m
= "$(DDOC_ENUM_MEMBERS ";
700 else if (sds
->isModule())
701 m
= "$(DDOC_MODULE_MEMBERS ";
703 size_t offset1
= buf
->length(); // save starting offset
705 size_t offset2
= buf
->length(); // to see if we write anything
709 for (size_t i
= 0; i
< sds
->members
->length
; i
++)
711 Dsymbol
*s
= (*sds
->members
)[i
];
712 //printf("\ts = '%s'\n", s->toChars());
714 // only expand if parent is a non-template (semantic won't work)
715 if (s
->comment
&& s
->isTemplateMixin() && s
->parent
&& !s
->parent
->isTemplateDeclaration())
716 expandTemplateMixinComments((TemplateMixin
*)s
, buf
, sc
);
718 emitComment(s
, buf
, sc
);
720 emitComment(NULL
, buf
, sc
);
724 if (buf
->length() == offset2
)
726 /* Didn't write out any members, so back out last write
728 buf
->setsize(offset1
);
731 buf
->writestring(")\n");
734 void emitProtection(OutBuffer
*buf
, Prot prot
)
736 if (prot
.kind
!= Prot::undefined
&& prot
.kind
!= Prot::public_
)
738 protectionToBuffer(buf
, prot
);
743 void emitComment(Dsymbol
*s
, OutBuffer
*buf
, Scope
*sc
)
745 class EmitComment
: public Visitor
751 EmitComment(OutBuffer
*buf
, Scope
*sc
)
756 void visit(Dsymbol
*) {}
757 void visit(InvariantDeclaration
*) {}
758 void visit(UnitTestDeclaration
*) {}
759 void visit(PostBlitDeclaration
*) {}
760 void visit(DtorDeclaration
*) {}
761 void visit(StaticCtorDeclaration
*) {}
762 void visit(StaticDtorDeclaration
*) {}
763 void visit(TypeInfoDeclaration
*) {}
765 void emit(Scope
*sc
, Dsymbol
*s
, const utf8_t
*com
)
767 if (s
&& sc
->lastdc
&& isDitto(com
))
769 sc
->lastdc
->a
.push(s
);
773 // Put previous doc comment if exists
774 if (DocComment
*dc
= sc
->lastdc
)
776 // Put the declaration signatures as the document 'title'
777 buf
->writestring(ddoc_decl_s
);
778 for (size_t i
= 0; i
< dc
->a
.length
; i
++)
780 Dsymbol
*sx
= dc
->a
[i
];
784 size_t o
= buf
->length();
785 toDocBuffer(sx
, buf
, sc
);
786 highlightCode(sc
, sx
, buf
, o
);
790 buf
->writestring("$(DDOC_DITTO ");
792 size_t o
= buf
->length();
793 toDocBuffer(sx
, buf
, sc
);
794 highlightCode(sc
, sx
, buf
, o
);
798 buf
->writestring(ddoc_decl_e
);
800 // Put the ddoc comment as the document 'description'
801 buf
->writestring(ddoc_decl_dd_s
);
803 dc
->writeSections(sc
, &dc
->a
, buf
);
804 if (ScopeDsymbol
*sds
= dc
->a
[0]->isScopeDsymbol())
805 emitMemberComments(sds
, buf
, sc
);
807 buf
->writestring(ddoc_decl_dd_e
);
808 //printf("buf.2 = [[%.*s]]\n", buf->length() - o0, buf->slice().ptr + o0);
813 DocComment
*dc
= DocComment::parse(s
, com
);
814 dc
->pmacrotable
= &sc
->_module
->macrotable
;
819 void visit(Declaration
*d
)
821 //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d->toChars(), d->comment);
822 //printf("type = %p\n", d->type);
823 const utf8_t
*com
= d
->comment
;
824 if (TemplateDeclaration
*td
= getEponymousParent(d
))
826 if (isDitto(td
->comment
))
829 com
= Lexer::combineComments(td
->comment
, com
);
835 if (!d
->type
&& !d
->isCtorDeclaration() && !d
->isAliasDeclaration())
837 if (d
->protection
.kind
== Prot::private_
|| sc
->protection
.kind
== Prot::private_
)
846 void visit(AggregateDeclaration
*ad
)
848 //printf("AggregateDeclaration::emitComment() '%s'\n", ad->toChars());
849 const utf8_t
*com
= ad
->comment
;
850 if (TemplateDeclaration
*td
= getEponymousParent(ad
))
852 if (isDitto(td
->comment
))
855 com
= Lexer::combineComments(td
->comment
, com
);
859 if (ad
->prot().kind
== Prot::private_
|| sc
->protection
.kind
== Prot::private_
)
870 void visit(TemplateDeclaration
*td
)
872 //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td->toChars(), td->kind());
873 if (td
->prot().kind
== Prot::private_
|| sc
->protection
.kind
== Prot::private_
)
878 if (Dsymbol
*ss
= getEponymousMember(td
))
883 emit(sc
, td
, td
->comment
);
886 void visit(EnumDeclaration
*ed
)
888 if (ed
->prot().kind
== Prot::private_
|| sc
->protection
.kind
== Prot::private_
)
890 if (ed
->isAnonymous() && ed
->members
)
892 for (size_t i
= 0; i
< ed
->members
->length
; i
++)
894 Dsymbol
*s
= (*ed
->members
)[i
];
895 emitComment(s
, buf
, sc
);
901 if (ed
->isAnonymous())
904 emit(sc
, ed
, ed
->comment
);
907 void visit(EnumMember
*em
)
909 //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em->toChars(), em->comment);
910 if (em
->prot().kind
== Prot::private_
|| sc
->protection
.kind
== Prot::private_
)
915 emit(sc
, em
, em
->comment
);
918 void visit(AttribDeclaration
*ad
)
920 //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
922 /* A general problem with this, illustrated by BUGZILLA 2516,
923 * is that attributes are not transmitted through to the underlying
924 * member declarations for template bodies, because semantic analysis
925 * is not done for template declaration bodies
926 * (only template instantiations).
927 * Hence, Ddoc omits attributes from template members.
930 Dsymbols
*d
= ad
->include(NULL
);
934 for (size_t i
= 0; i
< d
->length
; i
++)
936 Dsymbol
*s
= (*d
)[i
];
937 //printf("AttribDeclaration::emitComment %s\n", s->toChars());
938 emitComment(s
, buf
, sc
);
943 void visit(ProtDeclaration
*pd
)
949 sc
->protection
= pd
->protection
;
950 visit((AttribDeclaration
*)pd
);
951 scx
->lastdc
= sc
->lastdc
;
956 void visit(ConditionalDeclaration
*cd
)
958 //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
959 if (cd
->condition
->inc
)
961 visit((AttribDeclaration
*)cd
);
965 /* If generating doc comment, be careful because if we're inside
966 * a template, then include(NULL) will fail.
968 Dsymbols
*d
= cd
->decl
? cd
->decl
: cd
->elsedecl
;
969 for (size_t i
= 0; i
< d
->length
; i
++)
971 Dsymbol
*s
= (*d
)[i
];
972 emitComment(s
, buf
, sc
);
977 EmitComment
v(buf
, sc
);
980 v
.emit(sc
, NULL
, NULL
);
985 /******************************* toDocBuffer **********************************/
987 void toDocBuffer(Dsymbol
*s
, OutBuffer
*buf
, Scope
*sc
)
989 class ToDocBuffer
: public Visitor
995 ToDocBuffer(OutBuffer
*buf
, Scope
*sc
)
1000 void visit(Dsymbol
*s
)
1002 //printf("Dsymbol::toDocbuffer() %s\n", s->toChars());
1005 ::toCBuffer(s
, buf
, &hgs
);
1008 void prefix(Dsymbol
*s
)
1010 if (s
->isDeprecated())
1011 buf
->writestring("deprecated ");
1013 if (Declaration
*d
= s
->isDeclaration())
1015 emitProtection(buf
, d
->protection
);
1018 buf
->writestring("static ");
1019 else if (d
->isFinal())
1020 buf
->writestring("final ");
1021 else if (d
->isAbstract())
1022 buf
->writestring("abstract ");
1024 if (!d
->isFuncDeclaration()) // functionToBufferFull handles this
1027 buf
->writestring("const ");
1028 if (d
->isImmutable())
1029 buf
->writestring("immutable ");
1030 if (d
->isSynchronized())
1031 buf
->writestring("synchronized ");
1033 if (d
->storage_class
& STCmanifest
)
1034 buf
->writestring("enum ");
1039 void visit(Declaration
*d
)
1044 TemplateDeclaration
*td
= getEponymousParent(d
);
1045 //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d->toChars(), d->originalType ? d->originalType->toChars() : "--", td ? td->toChars() : "--");
1050 if (d
->isDeprecated())
1051 buf
->writestring("$(DEPRECATED ");
1057 Type
*origType
= d
->originalType
? d
->originalType
: d
->type
;
1058 if (origType
->ty
== Tfunction
)
1060 functionToBufferFull((TypeFunction
*)origType
, buf
, d
->ident
, &hgs
, td
);
1063 ::toCBuffer(origType
, buf
, d
->ident
, &hgs
);
1066 buf
->writestring(d
->ident
->toChars());
1068 if (d
->isVarDeclaration() && td
)
1070 buf
->writeByte('(');
1071 if (td
->origParameters
&& td
->origParameters
->length
)
1073 for (size_t i
= 0; i
< td
->origParameters
->length
; i
++)
1076 buf
->writestring(", ");
1077 toCBuffer((*td
->origParameters
)[i
], buf
, &hgs
);
1080 buf
->writeByte(')');
1083 // emit constraints if declaration is a templated declaration
1084 if (td
&& td
->constraint
)
1086 buf
->writestring(" if (");
1087 ::toCBuffer(td
->constraint
, buf
, &hgs
);
1088 buf
->writeByte(')');
1091 if (d
->isDeprecated())
1092 buf
->writestring(")");
1094 buf
->writestring(";\n");
1097 void visit(AliasDeclaration
*ad
)
1099 //printf("AliasDeclaration::toDocbuffer() %s\n", ad->toChars());
1103 if (ad
->isDeprecated())
1104 buf
->writestring("deprecated ");
1106 emitProtection(buf
, ad
->protection
);
1107 buf
->printf("alias %s = ", ad
->toChars());
1109 if (Dsymbol
*sa
= ad
->aliassym
) // ident alias
1111 prettyPrintDsymbol(sa
, ad
->parent
);
1113 else if (Type
*type
= ad
->getType()) // type alias
1115 if (type
->ty
== Tclass
|| type
->ty
== Tstruct
|| type
->ty
== Tenum
)
1117 if (Dsymbol
*s
= type
->toDsymbol(NULL
)) // elaborate type
1118 prettyPrintDsymbol(s
, ad
->parent
);
1120 buf
->writestring(type
->toChars());
1125 buf
->writestring(type
->toChars());
1129 buf
->writestring(";\n");
1132 void parentToBuffer(Dsymbol
*s
)
1134 if (s
&& !s
->isPackage() && !s
->isModule())
1136 parentToBuffer(s
->parent
);
1137 buf
->writestring(s
->toChars());
1138 buf
->writestring(".");
1142 static bool inSameModule(Dsymbol
*s
, Dsymbol
*p
)
1144 for ( ; s
; s
= s
->parent
)
1150 for ( ; p
; p
= p
->parent
)
1159 void prettyPrintDsymbol(Dsymbol
*s
, Dsymbol
*parent
)
1161 if (s
->parent
&& (s
->parent
== parent
)) // in current scope -> naked name
1163 buf
->writestring(s
->toChars());
1165 else if (!inSameModule(s
, parent
)) // in another module -> full name
1167 buf
->writestring(s
->toPrettyChars());
1169 else // nested in a type in this module -> full name w/o module name
1171 // if alias is nested in a user-type use module-scope lookup
1172 if (!parent
->isModule() && !parent
->isPackage())
1173 buf
->writestring(".");
1175 parentToBuffer(s
->parent
);
1176 buf
->writestring(s
->toChars());
1180 void visit(AggregateDeclaration
*ad
)
1185 buf
->printf("%s %s", ad
->kind(), ad
->toChars());
1186 buf
->writestring(";\n");
1189 void visit(StructDeclaration
*sd
)
1191 //printf("StructDeclaration::toDocbuffer() %s\n", sd->toChars());
1195 if (TemplateDeclaration
*td
= getEponymousParent(sd
))
1197 toDocBuffer(td
, buf
, sc
);
1201 buf
->printf("%s %s", sd
->kind(), sd
->toChars());
1203 buf
->writestring(";\n");
1206 void visit(ClassDeclaration
*cd
)
1208 //printf("ClassDeclaration::toDocbuffer() %s\n", cd->toChars());
1212 if (TemplateDeclaration
*td
= getEponymousParent(cd
))
1214 toDocBuffer(td
, buf
, sc
);
1218 if (!cd
->isInterfaceDeclaration() && cd
->isAbstract())
1219 buf
->writestring("abstract ");
1220 buf
->printf("%s %s", cd
->kind(), cd
->toChars());
1223 for (size_t i
= 0; i
< cd
->baseclasses
->length
; i
++)
1225 BaseClass
*bc
= (*cd
->baseclasses
)[i
];
1227 if (bc
->sym
&& bc
->sym
->ident
== Id::Object
)
1231 buf
->writestring(", ");
1234 buf
->writestring(": ");
1237 emitProtection(buf
, Prot(Prot::public_
));
1240 buf
->printf("$(DDOC_PSUPER_SYMBOL %s)", bc
->sym
->toPrettyChars());
1245 ::toCBuffer(bc
->type
, buf
, NULL
, &hgs
);
1248 buf
->writestring(";\n");
1251 void visit(EnumDeclaration
*ed
)
1256 buf
->printf("%s %s", ed
->kind(), ed
->toChars());
1259 buf
->writestring(": $(DDOC_ENUM_BASETYPE ");
1261 ::toCBuffer(ed
->memtype
, buf
, NULL
, &hgs
);
1262 buf
->writestring(")");
1264 buf
->writestring(";\n");
1267 void visit(EnumMember
*em
)
1272 buf
->writestring(em
->toChars());
1276 ToDocBuffer
v(buf
, sc
);
1280 /********************************* DocComment *********************************/
1282 DocComment
*DocComment::parse(Dsymbol
*s
, const utf8_t
*comment
)
1284 //printf("parse(%s): '%s'\n", s->toChars(), comment);
1285 DocComment
*dc
= new DocComment();
1290 dc
->parseSections(comment
);
1292 for (size_t i
= 0; i
< dc
->sections
.length
; i
++)
1294 Section
*sec
= dc
->sections
[i
];
1296 if (icmp("copyright", sec
->name
, sec
->namelen
) == 0)
1298 dc
->copyright
= sec
;
1300 if (icmp("macros", sec
->name
, sec
->namelen
) == 0)
1309 /*****************************************
1310 * Parse next paragraph out of *pcomment.
1311 * Update *pcomment to point past paragraph.
1312 * Returns NULL if no more paragraphs.
1313 * If paragraph ends in 'identifier:',
1314 * then (*pcomment)[0 .. idlen] is the identifier.
1317 void DocComment::parseSections(const utf8_t
*comment
)
1320 const utf8_t
*pstart
;
1322 const utf8_t
*idstart
= NULL
; // dead-store to prevent spurious warning
1325 const utf8_t
*name
= NULL
;
1328 //printf("parseSections('%s')\n", comment);
1332 const utf8_t
*pstart0
= p
;
1333 p
= skipwhitespace(p
);
1337 /* Find end of section, which is ended by one of:
1338 * 'identifier:' (but not inside a code section)
1345 // Check for start/end of a code section
1350 // restore leading indentation
1351 while (pstart0
< pstart
&& isIndentWS(pstart
-1)) --pstart
;
1360 // BUG: handle UTF PS and LS too
1361 if ((!*p
|| *p
== '\r' || *p
== '\n') && numdash
>= 3)
1366 if (!inCode
&& isIdStart(p
))
1368 const utf8_t
*q
= p
+ utfStride(p
);
1371 // Detected tag ends it
1372 if (*q
== ':' && isupper(*p
)
1373 && (isspace(q
[1]) || q
[1] == 0))
1377 for (pend
= p
; pend
> pstart
; pend
--)
1379 if (pend
[-1] == '\n')
1393 if (*p
== '\n' && !summary
&& !namelen
&& !inCode
)
1404 p
= skipwhitespace(p
);
1408 if (namelen
|| pstart
< pend
)
1411 if (icmp("Params", name
, namelen
) == 0)
1412 s
= new ParamSection();
1413 else if (icmp("Macros", name
, namelen
) == 0)
1414 s
= new MacroSection();
1418 s
->namelen
= namelen
;
1420 s
->bodylen
= pend
- pstart
;
1423 //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
1427 if (!summary
&& !namelen
)
1446 void DocComment::writeSections(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
)
1450 //printf("DocComment::writeSections()\n");
1451 Loc loc
= (*a
)[0]->loc
;
1452 if (Module
*m
= (*a
)[0]->isModule())
1458 size_t offset1
= buf
->length();
1459 buf
->writestring("$(DDOC_SECTIONS ");
1460 size_t offset2
= buf
->length();
1462 for (size_t i
= 0; i
< sections
.length
; i
++)
1464 Section
*sec
= sections
[i
];
1468 //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
1469 if (!sec
->namelen
&& i
== 0)
1471 buf
->writestring("$(DDOC_SUMMARY ");
1472 size_t o
= buf
->length();
1473 buf
->write(sec
->body
, sec
->bodylen
);
1474 escapeStrayParenthesis(loc
, buf
, o
);
1475 highlightText(sc
, a
, buf
, o
);
1476 buf
->writestring(")\n");
1479 sec
->write(loc
, this, sc
, a
, buf
);
1482 for (size_t i
= 0; i
< a
->length
; i
++)
1484 Dsymbol
*s
= (*a
)[i
];
1485 if (Dsymbol
*td
= getEponymousParent(s
))
1488 for (UnitTestDeclaration
*utd
= s
->ddocUnittest
; utd
; utd
= utd
->ddocUnittest
)
1490 if (utd
->protection
.kind
== Prot::private_
|| !utd
->comment
|| !utd
->fbody
)
1493 // Strip whitespaces to avoid showing empty summary
1494 const utf8_t
*c
= utd
->comment
;
1495 while (*c
== ' ' || *c
== '\t' || *c
== '\n' || *c
== '\r') ++c
;
1497 buf
->writestring("$(DDOC_EXAMPLES ");
1499 size_t o
= buf
->length();
1500 buf
->writestring((const char *)c
);
1504 size_t n
= getCodeIndent(utd
->codedoc
);
1505 while (n
--) buf
->writeByte(' ');
1506 buf
->writestring("----\n");
1507 buf
->writestring(utd
->codedoc
);
1508 buf
->writestring("----\n");
1509 highlightText(sc
, a
, buf
, o
);
1512 buf
->writestring(")");
1516 if (buf
->length() == offset2
)
1518 /* Didn't write out any sections, so back out last write
1520 buf
->setsize(offset1
);
1521 buf
->writestring("$(DDOC_BLANKLINE)\n");
1524 buf
->writestring(")\n");
1527 /***************************************************
1530 void Section::write(Loc loc
, DocComment
*, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
)
1536 static const char *table
[] =
1538 "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
1539 "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
1540 "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
1544 for (size_t i
= 0; table
[i
]; i
++)
1546 if (icmp(table
[i
], name
, namelen
) == 0)
1548 buf
->printf("$(DDOC_%s ", table
[i
]);
1553 buf
->writestring("$(DDOC_SECTION ");
1555 // Replace _ characters with spaces
1556 buf
->writestring("$(DDOC_SECTION_H ");
1557 size_t o
= buf
->length();
1558 for (size_t u
= 0; u
< namelen
; u
++)
1561 buf
->writeByte((c
== '_') ? ' ' : c
);
1563 escapeStrayParenthesis(loc
, buf
, o
);
1564 buf
->writestring(":)\n");
1568 buf
->writestring("$(DDOC_DESCRIPTION ");
1571 size_t o
= buf
->length();
1572 buf
->write(body
, bodylen
);
1573 escapeStrayParenthesis(loc
, buf
, o
);
1574 highlightText(sc
, a
, buf
, o
);
1575 buf
->writestring(")\n");
1578 /***************************************************
1581 void ParamSection::write(Loc loc
, DocComment
*, Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
)
1584 Dsymbol
*s
= (*a
)[0]; // test
1586 const utf8_t
*p
= body
;
1587 size_t len
= bodylen
;
1588 const utf8_t
*pend
= p
+ len
;
1590 const utf8_t
*tempstart
= NULL
;
1593 const utf8_t
*namestart
= NULL
;
1594 size_t namelen
= 0; // !=0 if line continuation
1596 const utf8_t
*textstart
= NULL
;
1599 size_t paramcount
= 0;
1601 buf
->writestring("$(DDOC_PARAMS ");
1604 // Skip to start of macro
1619 if (isIdStart(p
) || isCVariadicArg(p
, pend
- p
))
1622 goto Ltext
; // continuation of prev macro
1631 if (isCVariadicArg(p
, pend
- p
))
1634 templen
= p
- tempstart
;
1636 while (*p
== ' ' || *p
== '\t')
1642 goto Ltext
; // continuation of prev macro
1649 // Output existing param
1652 //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1655 buf
->writestring("$(DDOC_PARAM_ROW ");
1657 buf
->writestring("$(DDOC_PARAM_ID ");
1659 size_t o
= buf
->length();
1660 Parameter
*fparam
= isFunctionParameter(a
, namestart
, namelen
);
1663 // Comments on a template might refer to function parameters within.
1664 // Search the parameters of nested eponymous functions (with the same name.)
1665 fparam
= isEponymousFunctionParameter(a
, namestart
, namelen
);
1667 bool isCVariadic
= isCVariadicParameter(a
, namestart
, namelen
);
1670 buf
->writestring("...");
1672 else if (fparam
&& fparam
->type
&& fparam
->ident
)
1674 ::toCBuffer(fparam
->type
, buf
, fparam
->ident
, &hgs
);
1678 if (isTemplateParameter(a
, namestart
, namelen
))
1680 // 10236: Don't count template parameters for params check
1685 warning(s
->loc
, "Ddoc: function declaration has no parameter '%.*s'", (int)namelen
, namestart
);
1687 buf
->write(namestart
, namelen
);
1689 escapeStrayParenthesis(loc
, buf
, o
);
1690 highlightCode(sc
, a
, buf
, o
);
1692 buf
->writestring(")\n");
1694 buf
->writestring("$(DDOC_PARAM_DESC ");
1696 size_t o
= buf
->length();
1697 buf
->write(textstart
, textlen
);
1698 escapeStrayParenthesis(loc
, buf
, o
);
1699 highlightText(sc
, a
, buf
, o
);
1701 buf
->writestring(")");
1703 buf
->writestring(")\n");
1709 namestart
= tempstart
;
1712 while (*p
== ' ' || *p
== '\t')
1719 textlen
= p
- textstart
;
1727 while (*p
++ != '\n')
1731 goto L1
; // write out last one
1732 buf
->writestring(")\n");
1734 TypeFunction
*tf
= a
->length
== 1 ? isTypeFunction(s
) : NULL
;
1737 size_t pcount
= (tf
->parameterList
.parameters
? tf
->parameterList
.parameters
->length
: 0) +
1738 (int)(tf
->parameterList
.varargs
== VARARGvariadic
);
1739 if (pcount
!= paramcount
)
1741 warning(s
->loc
, "Ddoc: parameter count mismatch");
1746 /***************************************************
1749 void MacroSection::write(Loc
, DocComment
*dc
, Scope
*, Dsymbols
*, OutBuffer
*)
1751 //printf("MacroSection::write()\n");
1752 DocComment::parseMacros(dc
->pescapetable
, dc
->pmacrotable
, body
, bodylen
);
1755 /************************************************
1756 * Parse macros out of Macros: section.
1757 * Macros are of the form:
1763 void DocComment::parseMacros(Escape
**pescapetable
, Macro
**pmacrotable
, const utf8_t
*m
, size_t mlen
)
1765 const utf8_t
*p
= m
;
1767 const utf8_t
*pend
= p
+ len
;
1769 const utf8_t
*tempstart
= NULL
;
1772 const utf8_t
*namestart
= NULL
;
1773 size_t namelen
= 0; // !=0 if line continuation
1775 const utf8_t
*textstart
= NULL
;
1780 // Skip to start of macro
1801 goto Ltext
; // continuation of prev macro
1816 templen
= p
- tempstart
;
1822 if (!(*p
== ' ' || *p
== '\t'))
1830 goto Ltext
; // continuation of prev macro
1839 // Output existing macro
1841 //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1842 if (icmp("ESCAPES", namestart
, namelen
) == 0)
1843 parseEscapes(pescapetable
, textstart
, textlen
);
1845 Macro::define(pmacrotable
, namestart
, namelen
, textstart
, textlen
);
1851 namestart
= tempstart
;
1854 while (p
< pend
&& (*p
== ' ' || *p
== '\t'))
1859 while (p
< pend
&& *p
!= '\r' && *p
!= '\n')
1861 textlen
= p
- textstart
;
1864 //printf("p = %p, pend = %p\n", p, pend);
1871 while (p
< pend
&& *p
!= '\r' && *p
!= '\n')
1876 goto L1
; // write out last one
1879 /**************************************
1880 * Parse escapes of the form:
1882 * where c is a single character.
1883 * Multiple escapes can be separated
1884 * by whitespace and/or commas.
1887 void DocComment::parseEscapes(Escape
**pescapetable
, const utf8_t
*textstart
, size_t textlen
)
1889 Escape
*escapetable
= *pescapetable
;
1893 escapetable
= new Escape
;
1894 memset(escapetable
, 0, sizeof(Escape
));
1895 *pescapetable
= escapetable
;
1897 //printf("parseEscapes('%.*s') pescapetable = %p\n", textlen, textstart, pescapetable);
1898 const utf8_t
*p
= textstart
;
1899 const utf8_t
*pend
= p
+ textlen
;
1907 if (!(*p
== ' ' || *p
== '\t' || *p
== '\r' || *p
== '\n' || *p
== ','))
1911 if (p
[0] != '/' || p
[2] != '/')
1915 const utf8_t
*start
= p
;
1924 size_t len
= p
- start
;
1925 char *s
= (char *)memcpy(mem
.xmalloc(len
+ 1), start
, len
);
1927 escapetable
->strings
[c
] = s
;
1928 //printf("\t%c = '%s'\n", c, s);
1934 /******************************************
1935 * Compare 0-terminated string with length terminated string.
1936 * Return < 0, ==0, > 0
1939 int cmp(const char *stringz
, const void *s
, size_t slen
)
1941 size_t len1
= strlen(stringz
);
1944 return (int)(len1
- slen
);
1945 return memcmp(stringz
, s
, slen
);
1948 int icmp(const char *stringz
, const void *s
, size_t slen
)
1950 size_t len1
= strlen(stringz
);
1953 return (int)(len1
- slen
);
1954 return Port::memicmp(stringz
, (const char *)s
, slen
);
1957 /*****************************************
1958 * Return true if comment consists entirely of "ditto".
1961 bool isDitto(const utf8_t
*comment
)
1965 const utf8_t
*p
= skipwhitespace(comment
);
1967 if (Port::memicmp((const char *)p
, "ditto", 5) == 0 && *skipwhitespace(p
+ 5) == 0)
1973 /**********************************************
1977 const utf8_t
*skipwhitespace(const utf8_t
*p
)
1994 /************************************************
1995 * Scan forward to one of:
1996 * start of identifier
1997 * beginning of next line
2001 size_t skiptoident(OutBuffer
*buf
, size_t i
)
2003 while (i
< buf
->length())
2008 if (utf_decodeChar((utf8_t
*)buf
->slice().ptr
, buf
->length(), &i
, &c
))
2010 /* Ignore UTF errors, but still consume input
2019 else if (!(isalpha(c
) || c
== '_' || c
== '\n'))
2027 /************************************************
2028 * Scan forward past end of identifier.
2031 size_t skippastident(OutBuffer
*buf
, size_t i
)
2033 while (i
< buf
->length())
2038 if (utf_decodeChar((utf8_t
*)buf
->slice().ptr
, buf
->length(), &i
, &c
))
2040 /* Ignore UTF errors, but still consume input
2049 else if (isalnum(c
) || c
== '_')
2058 /************************************************
2059 * Scan forward past URL starting at i.
2060 * We don't want to highlight parts of a URL.
2063 * index just past it if it is a URL
2066 size_t skippastURL(OutBuffer
*buf
, size_t i
)
2068 size_t length
= buf
->length() - i
;
2069 utf8_t
*p
= (utf8_t
*)&buf
->slice().ptr
[i
];
2071 unsigned sawdot
= 0;
2073 if (length
> 7 && Port::memicmp((char *)p
, "http://", 7) == 0)
2077 else if (length
> 8 && Port::memicmp((char *)p
, "https://", 8) == 0)
2084 for (; j
< length
; j
++)
2089 if (c
== '-' || c
== '_' || c
== '?' ||
2090 c
== '=' || c
== '%' || c
== '&' ||
2091 c
== '/' || c
== '+' || c
== '#' ||
2109 /****************************************************
2112 bool isIdentifier(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
2114 for (size_t i
= 0; i
< a
->length
; i
++)
2116 const char *s
= (*a
)[i
]->ident
->toChars();
2117 if (cmp(s
, p
, len
) == 0)
2123 /****************************************************
2126 bool isKeyword(utf8_t
*p
, size_t len
)
2128 static const char *table
[] = { "true", "false", "null", NULL
};
2130 for (int i
= 0; table
[i
]; i
++)
2132 if (cmp(table
[i
], p
, len
) == 0)
2138 /****************************************************
2141 TypeFunction
*isTypeFunction(Dsymbol
*s
)
2143 FuncDeclaration
*f
= s
->isFuncDeclaration();
2145 /* f->type may be NULL for template members.
2149 Type
*t
= f
->originalType
? f
->originalType
: f
->type
;
2150 if (t
->ty
== Tfunction
)
2151 return (TypeFunction
*)t
;
2156 /****************************************************
2159 Parameter
*isFunctionParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
2161 for (size_t i
= 0; i
< a
->length
; i
++)
2163 Parameter
*fparam
= isFunctionParameter((*a
)[i
], p
, len
);
2172 /****************************************************
2175 TemplateParameter
*isTemplateParameter(Dsymbols
*a
, const utf8_t
*p
, size_t len
)
2177 for (size_t i
= 0; i
< a
->length
; i
++)
2179 TemplateDeclaration
*td
= (*a
)[i
]->isTemplateDeclaration();
2180 // Check for the parent, if the current symbol is not a template declaration.
2182 td
= getEponymousParent((*a
)[i
]);
2183 if (td
&& td
->origParameters
)
2185 for (size_t k
= 0; k
< td
->origParameters
->length
; k
++)
2187 TemplateParameter
*tp
= (*td
->origParameters
)[k
];
2188 if (tp
->ident
&& cmp(tp
->ident
->toChars(), p
, len
) == 0)
2198 /****************************************************
2199 * Return true if str is a reserved symbol name
2200 * that starts with a double underscore.
2203 bool isReservedName(utf8_t
*str
, size_t len
)
2205 static const char *table
[] = {
2206 "__ctor", "__dtor", "__postblit", "__invariant", "__unitTest",
2207 "__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result",
2208 "__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp",
2209 "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
2210 "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
2211 "__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", NULL
};
2213 for (int i
= 0; table
[i
]; i
++)
2215 if (cmp(table
[i
], str
, len
) == 0)
2221 /**************************************************
2222 * Highlight text section.
2225 void highlightText(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
)
2227 Dsymbol
*s
= a
->length
? (*a
)[0] : NULL
; // test
2229 //printf("highlightText()\n");
2231 int leadingBlank
= 1;
2234 //int inComment = 0; // in <!-- ... --> comment
2235 size_t iCodeStart
= 0; // start of code section
2236 size_t codeIndent
= 0;
2238 size_t iLineStart
= offset
;
2240 for (size_t i
= offset
; i
< buf
->length(); i
++)
2242 utf8_t c
= buf
->slice().ptr
[i
];
2254 // `inline code` is only valid if contained on a single line
2255 // otherwise, the backticks should be output literally.
2257 // This lets things like `output from the linker' display
2258 // unmolested while keeping the feature consistent with GitHub.
2261 inCode
= false; // the backtick also assumes we're in code
2263 // Nothing else is necessary since the DDOC_BACKQUOTED macro is
2264 // inserted lazily at the close quote, meaning the rest of the
2265 // text is already OK.
2268 if (!sc
->_module
->isDocFile
&&
2269 !inCode
&& i
== iLineStart
&& i
+ 1 < buf
->length()) // if "\n\n"
2271 static const char blankline
[] = "$(DDOC_BLANKLINE)\n";
2273 i
= buf
->insert(i
, blankline
, strlen(blankline
));
2284 utf8_t
*p
= (utf8_t
*)&buf
->slice().ptr
[i
];
2285 const char *se
= sc
->_module
->escapetable
->escapeChar('<');
2286 if (se
&& strcmp(se
, "<") == 0)
2289 // Skip over comments
2290 if (p
[1] == '!' && p
[2] == '-' && p
[3] == '-')
2296 if (j
== buf
->length())
2298 if (p
[0] == '-' && p
[1] == '-' && p
[2] == '>')
2300 i
= j
+ 2; // place on closing '>'
2309 // Skip over HTML tag
2310 if (isalpha(p
[1]) || (p
[1] == '/' && isalpha(p
[2])))
2316 if (j
== buf
->length())
2320 i
= j
; // place on closing '>'
2330 // Replace '<' with '<' character entity
2333 size_t len
= strlen(se
);
2335 i
= buf
->insert(i
, se
, len
);
2336 i
--; // point to ';'
2345 // Replace '>' with '>' character entity
2346 const char *se
= sc
->_module
->escapetable
->escapeChar('>');
2349 size_t len
= strlen(se
);
2351 i
= buf
->insert(i
, se
, len
);
2352 i
--; // point to ';'
2361 utf8_t
*p
= (utf8_t
*)&buf
->slice().ptr
[i
];
2362 if (p
[1] == '#' || isalpha(p
[1]))
2363 break; // already a character entity
2364 // Replace '&' with '&' character entity
2365 const char *se
= sc
->_module
->escapetable
->escapeChar('&');
2368 size_t len
= strlen(se
);
2370 i
= buf
->insert(i
, se
, len
);
2371 i
--; // point to ';'
2384 codebuf
.write(buf
->slice().ptr
+ iCodeStart
+ 1, i
- (iCodeStart
+ 1));
2386 // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
2387 highlightCode(sc
, a
, &codebuf
, 0);
2389 buf
->remove(iCodeStart
, i
- iCodeStart
+ 1); // also trimming off the current `
2391 static const char pre
[] = "$(DDOC_BACKQUOTED ";
2392 i
= buf
->insert(iCodeStart
, pre
, strlen(pre
));
2393 i
= buf
->insert(i
, (char *)codebuf
.slice().ptr
, codebuf
.length());
2394 i
= buf
->insert(i
, ")", 1);
2396 i
--; // point to the ending ) so when the for loop does i++, it will see the next character
2406 codeIndent
= 0; // inline code is not indented
2408 // All we do here is set the code flags and record
2409 // the location. The macro will be inserted lazily
2410 // so we can easily cancel the inBacktick if we come
2411 // across a newline character.
2417 /* A line beginning with --- delimits a code section.
2418 * inCode tells us if it is start or end of a code section.
2429 if (i
>= buf
->length())
2431 c
= buf
->slice().ptr
[i
];
2440 if (i
+ 1 >= buf
->length())
2442 if (buf
->slice().ptr
[i
+ 1] == '\n')
2448 // BUG: handle UTF PS and LS too
2455 // We have the start/end of a code section
2457 // Remove the entire --- line, including blanks and \n
2458 buf
->remove(iLineStart
, i
- iLineStart
+ eollen
);
2461 if (inCode
&& (i
<= iCodeStart
))
2463 // Empty code section, just remove it completely.
2471 // The code section is from iCodeStart to i
2474 codebuf
.write(buf
->slice().ptr
+ iCodeStart
, i
- iCodeStart
);
2475 codebuf
.writeByte(0);
2477 // Remove leading indentations from all lines
2478 bool lineStart
= true;
2479 utf8_t
*endp
= (utf8_t
*)codebuf
.slice().ptr
+ codebuf
.length();
2480 for (utf8_t
*p
= (utf8_t
*)codebuf
.slice().ptr
; p
< endp
; )
2484 size_t j
= codeIndent
;
2486 while (j
-- > 0 && q
< endp
&& isIndentWS(q
))
2488 codebuf
.remove(p
- (utf8_t
*)codebuf
.slice().ptr
, q
- p
);
2489 assert((utf8_t
*)codebuf
.slice().ptr
<= p
);
2490 assert(p
< (utf8_t
*)codebuf
.slice().ptr
+ codebuf
.length());
2492 endp
= (utf8_t
*)codebuf
.slice().ptr
+ codebuf
.length(); // update
2500 highlightCode2(sc
, a
, &codebuf
, 0);
2501 buf
->remove(iCodeStart
, i
- iCodeStart
);
2502 i
= buf
->insert(iCodeStart
, codebuf
.slice().ptr
, codebuf
.length());
2503 i
= buf
->insert(i
, (const char *)")\n", 2);
2504 i
-= 2; // in next loop, c should be '\n'
2508 static const char d_code
[] = "$(D_CODE ";
2511 codeIndent
= istart
- iLineStart
; // save indent count
2512 i
= buf
->insert(i
, d_code
, strlen(d_code
));
2514 i
--; // place i on >
2515 leadingBlank
= true;
2522 if (sc
->_module
->isDocFile
|| inCode
)
2525 utf8_t
*start
= (utf8_t
*)buf
->slice().ptr
+ i
;
2526 if (isIdStart(start
))
2528 size_t j
= skippastident(buf
, i
);
2531 size_t k
= skippastURL(buf
, i
);
2542 // leading '_' means no highlight unless it's a reserved symbol name
2544 (i
== 0 || !isdigit(*(start
- 1))) &&
2545 (i
== buf
->length() - 1 || !isReservedName(start
, len
)))
2551 if (isIdentifier(a
, start
, len
))
2553 i
= buf
->bracket(i
, "$(DDOC_PSYMBOL ", j
, ")") - 1;
2556 if (isKeyword(start
, len
))
2558 i
= buf
->bracket(i
, "$(DDOC_KEYWORD ", j
, ")") - 1;
2561 if (isFunctionParameter(a
, start
, len
))
2563 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2564 i
= buf
->bracket(i
, "$(DDOC_PARAM ", j
, ")") - 1;
2574 error(s
? s
->loc
: Loc(), "unmatched --- in DDoc comment");
2577 /**************************************************
2578 * Highlight code for DDOC section.
2581 void highlightCode(Scope
*sc
, Dsymbol
*s
, OutBuffer
*buf
, size_t offset
)
2583 //printf("highlightCode(s = %s '%s')\n", s->kind(), s->toChars());
2585 emitAnchor(&ancbuf
, s
, sc
);
2586 buf
->insert(offset
, (char *)ancbuf
.slice().ptr
, ancbuf
.length());
2587 offset
+= ancbuf
.length();
2591 highlightCode(sc
, &a
, buf
, offset
);
2594 /****************************************************
2597 void highlightCode(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
)
2599 //printf("highlightCode(a = '%s')\n", a->toChars());
2601 for (size_t i
= offset
; i
< buf
->length(); i
++)
2603 utf8_t c
= buf
->slice().ptr
[i
];
2604 const char *se
= sc
->_module
->escapetable
->escapeChar(c
);
2607 size_t len
= strlen(se
);
2609 i
= buf
->insert(i
, se
, len
);
2610 i
--; // point to ';'
2614 utf8_t
*start
= (utf8_t
*)buf
->slice().ptr
+ i
;
2615 if (isIdStart(start
))
2617 size_t j
= skippastident(buf
, i
);
2621 if (isIdentifier(a
, start
, len
))
2623 i
= buf
->bracket(i
, "$(DDOC_PSYMBOL ", j
, ")") - 1;
2626 if (isFunctionParameter(a
, start
, len
))
2628 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2629 i
= buf
->bracket(i
, "$(DDOC_PARAM ", j
, ")") - 1;
2638 /****************************************
2641 void highlightCode3(Scope
*sc
, OutBuffer
*buf
, const utf8_t
*p
, const utf8_t
*pend
)
2643 for (; p
< pend
; p
++)
2645 const char *s
= sc
->_module
->escapetable
->escapeChar(*p
);
2647 buf
->writestring(s
);
2653 /**************************************************
2654 * Highlight code for CODE section.
2657 void highlightCode2(Scope
*sc
, Dsymbols
*a
, OutBuffer
*buf
, size_t offset
)
2659 unsigned errorsave
= global
.errors
;
2660 Lexer
lex(NULL
, (utf8_t
*)buf
->slice().ptr
, 0, buf
->length() - 1, 0, 1);
2662 const utf8_t
*lastp
= (utf8_t
*)buf
->slice().ptr
;
2664 //printf("highlightCode2('%.*s')\n", buf->length() - 1, buf->slice().ptr);
2665 res
.reserve(buf
->length());
2670 highlightCode3(sc
, &res
, lastp
, tok
.ptr
);
2672 const char *highlight
= NULL
;
2679 size_t len
= lex
.p
- tok
.ptr
;
2680 if (isIdentifier(a
, tok
.ptr
, len
))
2682 highlight
= "$(D_PSYMBOL ";
2685 if (isFunctionParameter(a
, tok
.ptr
, len
))
2687 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2688 highlight
= "$(D_PARAM ";
2694 highlight
= "$(D_COMMENT ";
2698 highlight
= "$(D_STRING ";
2702 if (tok
.isKeyword())
2703 highlight
= "$(D_KEYWORD ";
2708 res
.writestring(highlight
);
2709 size_t o
= res
.length();
2710 highlightCode3(sc
, &res
, tok
.ptr
, lex
.p
);
2711 if (tok
.value
== TOKcomment
|| tok
.value
== TOKstring
)
2712 escapeDdocString(&res
, o
); // Bugzilla 7656, 7715, and 10519
2716 highlightCode3(sc
, &res
, tok
.ptr
, lex
.p
);
2717 if (tok
.value
== TOKeof
)
2721 buf
->setsize(offset
);
2723 global
.errors
= errorsave
;
2726 /***************************************
2727 * Find character string to replace c with.
2730 const char *Escape::escapeChar(unsigned c
)
2733 //printf("escapeChar('%c') => %p, %p\n", c, strings, strings[c]);
2737 /****************************************
2738 * Determine if p points to the start of a "..." parameter identifier.
2741 bool isCVariadicArg(const utf8_t
*p
, size_t len
)
2743 return len
>= 3 && cmp("...", p
, 3) == 0;
2746 /****************************************
2747 * Determine if p points to the start of an identifier.
2750 bool isIdStart(const utf8_t
*p
)
2753 if (isalpha(c
) || c
== '_')
2758 if (utf_decodeChar(p
, 4, &i
, &c
))
2759 return false; // ignore errors
2766 /****************************************
2767 * Determine if p points to the rest of an identifier.
2770 bool isIdTail(const utf8_t
*p
)
2773 if (isalnum(c
) || c
== '_')
2778 if (utf_decodeChar(p
, 4, &i
, &c
))
2779 return false; // ignore errors
2786 /****************************************
2787 * Determine if p points to the indentation space.
2790 bool isIndentWS(const utf8_t
*p
)
2792 return (*p
== ' ') || (*p
== '\t');
2795 /*****************************************
2796 * Return number of bytes in UTF character.
2799 int utfStride(const utf8_t
*p
)
2805 utf_decodeChar(p
, 4, &i
, &c
); // ignore errors, but still consume input