]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/d/dmd/doc.c
Merge dmd upstream 6243fa6d2
[thirdparty/gcc.git] / gcc / d / dmd / doc.c
CommitLineData
b4c522fa
IB
1
2/* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2018 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
9 */
10
11// This implements the Ddoc capability.
12
f9ab59ff 13#include "root/dsystem.h"
b4c522fa
IB
14#include "root/rmem.h"
15#include "root/root.h"
16#include "root/port.h"
17#include "root/aav.h"
18
19#include "attrib.h"
20#include "cond.h"
21#include "mars.h"
22#include "dsymbol.h"
23#include "macro.h"
24#include "template.h"
25#include "lexer.h"
26#include "aggregate.h"
27#include "declaration.h"
28#include "statement.h"
29#include "enum.h"
30#include "id.h"
31#include "module.h"
32#include "scope.h"
33#include "hdrgen.h"
34#include "doc.h"
35#include "mtype.h"
36#include "utf.h"
37
38void emitMemberComments(ScopeDsymbol *sds, OutBuffer *buf, Scope *sc);
39void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc);
40void emitComment(Dsymbol *s, OutBuffer *buf, Scope *sc);
41
42struct Escape
43{
44 const char *strings[256];
45
46 const char *escapeChar(unsigned c);
47};
48
49class Section
50{
51public:
52 const utf8_t *name;
53 size_t namelen;
54
55 const utf8_t *body;
56 size_t bodylen;
57
58 int nooutput;
59
60 virtual void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf);
61};
62
63class ParamSection : public Section
64{
65public:
66 void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf);
67};
68
69class MacroSection : public Section
70{
71public:
72 void write(Loc loc, DocComment *dc, Scope *sc, Dsymbols *a, OutBuffer *buf);
73};
74
75typedef Array<Section *> Sections;
76
77struct DocComment
78{
79 Sections sections; // Section*[]
80
81 Section *summary;
82 Section *copyright;
83 Section *macros;
84 Macro **pmacrotable;
85 Escape **pescapetable;
86
87 Dsymbols a;
88
89 DocComment() :
90 summary(NULL), copyright(NULL), macros(NULL), pmacrotable(NULL), pescapetable(NULL)
91 { }
92
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);
96
97 void parseSections(const utf8_t *comment);
98 void writeSections(Scope *sc, Dsymbols *a, OutBuffer *buf);
99};
100
101
102int cmp(const char *stringz, const void *s, size_t slen);
103int icmp(const char *stringz, const void *s, size_t slen);
104bool isDitto(const utf8_t *comment);
105const utf8_t *skipwhitespace(const utf8_t *p);
106size_t skiptoident(OutBuffer *buf, size_t i);
107size_t skippastident(OutBuffer *buf, size_t i);
108size_t skippastURL(OutBuffer *buf, size_t i);
109void highlightText(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset);
110void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset);
111void highlightCode(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset);
112void highlightCode2(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset);
113void highlightCode3(Scope *sc, OutBuffer *buf, const utf8_t *p, const utf8_t *pend);
114TypeFunction *isTypeFunction(Dsymbol *s);
115Parameter *isFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len);
116TemplateParameter *isTemplateParameter(Dsymbols *a, const utf8_t *p, size_t len);
117
118bool isIdStart(const utf8_t *p);
119bool isCVariadicArg(const utf8_t *p, size_t len);
120bool isIdTail(const utf8_t *p);
121bool isIndentWS(const utf8_t *p);
122int utfStride(const utf8_t *p);
123
124// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one).
125bool isCVariadicParameter(Dsymbols *a, const utf8_t *p, size_t len)
126{
127 for (size_t i = 0; i < a->dim; i++)
128 {
129 TypeFunction *tf = isTypeFunction((*a)[i]);
130 if (tf && tf->varargs == 1 && cmp("...", p, len) == 0)
131 return true;
132 }
133 return false;
134}
135
136static Dsymbol *getEponymousMember(TemplateDeclaration *td)
137{
138 if (!td->onemember)
139 return NULL;
140
141 if (AggregateDeclaration *ad = td->onemember->isAggregateDeclaration())
142 return ad;
143 if (FuncDeclaration *fd = td->onemember->isFuncDeclaration())
144 return fd;
145 if (td->onemember->isEnumMember())
146 return NULL; // Keep backward compatibility. See compilable/ddoc9.d
147 if (VarDeclaration *vd = td->onemember->isVarDeclaration())
148 return td->constraint ? NULL : vd;
149
150 return NULL;
151}
152
153static TemplateDeclaration *getEponymousParent(Dsymbol *s)
154{
155 if (!s->parent)
156 return NULL;
157 TemplateDeclaration *td = s->parent->isTemplateDeclaration();
158 return (td && getEponymousMember(td)) ? td : NULL;
159}
160
161static const char ddoc_default[] = "\
162DDOC = <html><head>\n\
163 <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
164 <title>$(TITLE)</title>\n\
165 </head><body>\n\
166 <h1>$(TITLE)</h1>\n\
167 $(BODY)\n\
168 <hr>$(SMALL Page generated by $(LINK2 http://dlang.org/ddoc.html, Ddoc). $(COPYRIGHT))\n\
169 </body></html>\n\
170\n\
171B = <b>$0</b>\n\
172I = <i>$0</i>\n\
173U = <u>$0</u>\n\
174P = <p>$0</p>\n\
175DL = <dl>$0</dl>\n\
176DT = <dt>$0</dt>\n\
177DD = <dd>$0</dd>\n\
178TABLE = <table>$0</table>\n\
179TR = <tr>$0</tr>\n\
180TH = <th>$0</th>\n\
181TD = <td>$0</td>\n\
182OL = <ol>$0</ol>\n\
183UL = <ul>$0</ul>\n\
184LI = <li>$0</li>\n\
185BIG = <big>$0</big>\n\
186SMALL = <small>$0</small>\n\
187BR = <br>\n\
188LINK = <a href=\"$0\">$0</a>\n\
189LINK2 = <a href=\"$1\">$+</a>\n\
190LPAREN= (\n\
191RPAREN= )\n\
192BACKTICK= `\n\
193DOLLAR= $\n\
194DEPRECATED= $0\n\
195\n\
196RED = <font color=red>$0</font>\n\
197BLUE = <font color=blue>$0</font>\n\
198GREEN = <font color=green>$0</font>\n\
199YELLOW =<font color=yellow>$0</font>\n\
200BLACK = <font color=black>$0</font>\n\
201WHITE = <font color=white>$0</font>\n\
202\n\
203D_CODE = <pre class=\"d_code\">$0</pre>\n\
204DDOC_BACKQUOTED = $(D_INLINECODE $0)\n\
205D_INLINECODE = <pre style=\"display:inline;\" class=\"d_inline_code\">$0</pre>\n\
206D_COMMENT = $(GREEN $0)\n\
207D_STRING = $(RED $0)\n\
208D_KEYWORD = $(BLUE $0)\n\
209D_PSYMBOL = $(U $0)\n\
210D_PARAM = $(I $0)\n\
211\n\
212DDOC_COMMENT = <!-- $0 -->\n\
213DDOC_DECL = $(DT $(BIG $0))\n\
214DDOC_DECL_DD = $(DD $0)\n\
215DDOC_DITTO = $(BR)$0\n\
216DDOC_SECTIONS = $0\n\
217DDOC_SUMMARY = $0$(BR)$(BR)\n\
218DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
219DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
220DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
221DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
222DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
223DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
224DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
225DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
226DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
227DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
228DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
229DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
230DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
231DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
232DDOC_SECTION_H = $(B $0)$(BR)\n\
233DDOC_SECTION = $0$(BR)$(BR)\n\
234DDOC_MEMBERS = $(DL $0)\n\
235DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
236DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\
237DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
238DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\
239DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
240DDOC_ENUM_BASETYPE = $0\n\
241DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
242DDOC_PARAM_ROW = $(TR $0)\n\
243DDOC_PARAM_ID = $(TD $0)\n\
244DDOC_PARAM_DESC = $(TD $0)\n\
245DDOC_BLANKLINE = $(BR)$(BR)\n\
246\n\
247DDOC_ANCHOR = <a name=\"$1\"></a>\n\
248DDOC_PSYMBOL = $(U $0)\n\
249DDOC_PSUPER_SYMBOL = $(U $0)\n\
250DDOC_KEYWORD = $(B $0)\n\
251DDOC_PARAM = $(I $0)\n\
252\n\
253ESCAPES = /</&lt;/\n\
254 />/&gt;/\n\
255 /&/&amp;/\n\
256";
257
258static const char ddoc_decl_s[] = "$(DDOC_DECL ";
259static const char ddoc_decl_e[] = ")\n";
260
261static const char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
262static const char ddoc_decl_dd_e[] = ")\n";
263
264
265/****************************************************
266 */
267
268void gendocfile(Module *m)
269{
270 static OutBuffer mbuf;
271 static int mbuf_done;
272
273 OutBuffer buf;
274
275 //printf("Module::gendocfile()\n");
276
277 if (!mbuf_done) // if not already read the ddoc files
278 {
279 mbuf_done = 1;
280
281 // Use our internal default
282 mbuf.write(ddoc_default, strlen(ddoc_default));
283
284 // Override with DDOCFILE specified in the sc.ini file
285 char *p = getenv("DDOCFILE");
286 if (p)
287 global.params.ddocfiles->shift(p);
288
289 // Override with the ddoc macro files from the command line
290 for (size_t i = 0; i < global.params.ddocfiles->dim; i++)
291 {
292 FileName f((*global.params.ddocfiles)[i]);
293 File file(&f);
294 readFile(m->loc, &file);
295 // BUG: convert file contents to UTF-8 before use
296
297 //printf("file: '%.*s'\n", file.len, file.buffer);
298 mbuf.write(file.buffer, file.len);
299 }
300 }
301 DocComment::parseMacros(&m->escapetable, &m->macrotable, (utf8_t *)mbuf.data, mbuf.offset);
302
303 Scope *sc = Scope::createGlobal(m); // create root scope
304
305 DocComment *dc = DocComment::parse(m, m->comment);
306 dc->pmacrotable = &m->macrotable;
307 dc->pescapetable = &m->escapetable;
308 sc->lastdc = dc;
309
310 // Generate predefined macros
311
312 // Set the title to be the name of the module
313 {
314 const char *p = m->toPrettyChars();
315 Macro::define(&m->macrotable, (const utf8_t *)"TITLE", 5, (const utf8_t *)p, strlen(p));
316 }
317
318 // Set time macros
319 {
320 time_t t;
321 time(&t);
322 char *p = ctime(&t);
323 p = mem.xstrdup(p);
324 Macro::define(&m->macrotable, (const utf8_t *)"DATETIME", 8, (const utf8_t *)p, strlen(p));
325 Macro::define(&m->macrotable, (const utf8_t *)"YEAR", 4, (const utf8_t *)p + 20, 4);
326 }
327
328 const char *srcfilename = m->srcfile->toChars();
329 Macro::define(&m->macrotable, (const utf8_t *)"SRCFILENAME", 11, (const utf8_t *)srcfilename, strlen(srcfilename));
330
331 const char *docfilename = m->docfile->toChars();
332 Macro::define(&m->macrotable, (const utf8_t *)"DOCFILENAME", 11, (const utf8_t *)docfilename, strlen(docfilename));
333
334 if (dc->copyright)
335 {
336 dc->copyright->nooutput = 1;
337 Macro::define(&m->macrotable, (const utf8_t *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen);
338 }
339
340 buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", m->srcfile->toChars());
341 if (m->isDocFile)
342 {
343 Loc loc = m->md ? m->md->loc : m->loc;
344 size_t commentlen = strlen((const char *)m->comment);
345 Dsymbols a;
346 // Bugzilla 9764: Don't push m in a, to prevent emphasize ddoc file name.
347 if (dc->macros)
348 {
349 commentlen = dc->macros->name - m->comment;
350 dc->macros->write(loc, dc, sc, &a, &buf);
351 }
352 buf.write(m->comment, commentlen);
353 highlightText(sc, &a, &buf, 0);
354 }
355 else
356 {
357 Dsymbols a;
358 a.push(m);
359 dc->writeSections(sc, &a, &buf);
360 emitMemberComments(m, &buf, sc);
361 }
362
363 //printf("BODY= '%.*s'\n", buf.offset, buf.data);
364 Macro::define(&m->macrotable, (const utf8_t *)"BODY", 4, (const utf8_t *)buf.data, buf.offset);
365
366 OutBuffer buf2;
367 buf2.writestring("$(DDOC)\n");
368 size_t end = buf2.offset;
369 m->macrotable->expand(&buf2, 0, &end, NULL, 0);
370
371 /* Remove all the escape sequences from buf2,
372 * and make CR-LF the newline.
373 */
374 {
375 buf.setsize(0);
376 buf.reserve(buf2.offset);
377 utf8_t *p = (utf8_t *)buf2.data;
378 for (size_t j = 0; j < buf2.offset; j++)
379 {
380 utf8_t c = p[j];
381 if (c == 0xFF && j + 1 < buf2.offset)
382 {
383 j++;
384 continue;
385 }
386 if (c == '\n')
387 buf.writeByte('\r');
388 else if (c == '\r')
389 {
390 buf.writestring("\r\n");
391 if (j + 1 < buf2.offset && p[j + 1] == '\n')
392 {
393 j++;
394 }
395 continue;
396 }
397 buf.writeByte(c);
398 }
399 }
400
401 // Transfer image to file
402 assert(m->docfile);
403 m->docfile->setbuffer(buf.data, buf.offset);
404 m->docfile->ref = 1;
405 ensurePathToNameExists(Loc(), m->docfile->toChars());
406 writeFile(m->loc, m->docfile);
407}
408
409/****************************************************
410 * Having unmatched parentheses can hose the output of Ddoc,
411 * as the macros depend on properly nested parentheses.
412 * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
413 * to preserve text literally. This also means macros in the
414 * text won't be expanded.
415 */
416void escapeDdocString(OutBuffer *buf, size_t start)
417{
418 for (size_t u = start; u < buf->offset; u++)
419 {
420 utf8_t c = buf->data[u];
421 switch(c)
422 {
423 case '$':
424 buf->remove(u, 1);
425 buf->insert(u, (const char *)"$(DOLLAR)", 9);
426 u += 8;
427 break;
428
429 case '(':
430 buf->remove(u, 1); //remove the (
431 buf->insert(u, (const char *)"$(LPAREN)", 9); //insert this instead
432 u += 8; //skip over newly inserted macro
433 break;
434
435 case ')':
436 buf->remove(u, 1); //remove the )
437 buf->insert(u, (const char *)"$(RPAREN)", 9); //insert this instead
438 u += 8; //skip over newly inserted macro
439 break;
440 }
441 }
442}
443
444/****************************************************
445 * Having unmatched parentheses can hose the output of Ddoc,
446 * as the macros depend on properly nested parentheses.
447
448 * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
449 */
450void escapeStrayParenthesis(Loc loc, OutBuffer *buf, size_t start)
451{
452 unsigned par_open = 0;
453
454 for (size_t u = start; u < buf->offset; u++)
455 {
456 utf8_t c = buf->data[u];
457 switch(c)
458 {
459 case '(':
460 par_open++;
461 break;
462
463 case ')':
464 if (par_open == 0)
465 {
466 //stray ')'
467 warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output."
468 " Use $(RPAREN) instead for unpaired right parentheses.");
469 buf->remove(u, 1); //remove the )
470 buf->insert(u, (const char *)"$(RPAREN)", 9); //insert this instead
471 u += 8; //skip over newly inserted macro
472 }
473 else
474 par_open--;
475 break;
476 }
477 }
478
479 if (par_open) // if any unmatched lparens
480 {
481 par_open = 0;
482 for (size_t u = buf->offset; u > start;)
483 {
484 u--;
485 utf8_t c = buf->data[u];
486 switch(c)
487 {
488 case ')':
489 par_open++;
490 break;
491
492 case '(':
493 if (par_open == 0)
494 {
495 //stray '('
496 warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output."
497 " Use $(LPAREN) instead for unpaired left parentheses.");
498 buf->remove(u, 1); //remove the (
499 buf->insert(u, (const char *)"$(LPAREN)", 9); //insert this instead
500 }
501 else
502 par_open--;
503 break;
504 }
505 }
506 }
507}
508
509// Basically, this is to skip over things like private{} blocks in a struct or
510// class definition that don't add any components to the qualified name.
511static Scope *skipNonQualScopes(Scope *sc)
512{
513 while (sc && !sc->scopesym)
514 sc = sc->enclosing;
515 return sc;
516}
517
518static bool emitAnchorName(OutBuffer *buf, Dsymbol *s, Scope *sc)
519{
520 if (!s || s->isPackage() || s->isModule())
521 return false;
522
523 // Add parent names first
524 bool dot = false;
525 if (s->parent)
526 dot = emitAnchorName(buf, s->parent, sc);
527 else if (sc)
528 dot = emitAnchorName(buf, sc->scopesym, skipNonQualScopes(sc->enclosing));
529
530 // Eponymous template members can share the parent anchor name
531 if (getEponymousParent(s))
532 return dot;
533 if (dot)
534 buf->writeByte('.');
535
536 // Use "this" not "__ctor"
537 TemplateDeclaration *td;
538 if (s->isCtorDeclaration() || ((td = s->isTemplateDeclaration()) != NULL &&
539 td->onemember && td->onemember->isCtorDeclaration()))
540 {
541 buf->writestring("this");
542 }
543 else
544 {
545 /* We just want the identifier, not overloads like TemplateDeclaration::toChars.
546 * We don't want the template parameter list and constraints. */
547 buf->writestring(s->Dsymbol::toChars());
548 }
549 return true;
550}
551
552static void emitAnchor(OutBuffer *buf, Dsymbol *s, Scope *sc)
553{
554 Identifier *ident;
555 {
556 OutBuffer anc;
557 emitAnchorName(&anc, s, skipNonQualScopes(sc));
558 ident = Identifier::idPool(anc.peekString());
559 }
560 size_t *count = (size_t*)dmd_aaGet(&sc->anchorCounts, (void *)ident);
561 TemplateDeclaration *td = getEponymousParent(s);
562 // don't write an anchor for matching consecutive ditto symbols
563 if (*count > 0 && sc->prevAnchor == ident &&
564 sc->lastdc && (isDitto(s->comment) || (td && isDitto(td->comment))))
565 return;
566
567 (*count)++;
568 // cache anchor name
569 sc->prevAnchor = ident;
570
571 buf->writestring("$(DDOC_ANCHOR ");
572 buf->writestring(ident->toChars());
573 // only append count once there's a duplicate
574 if (*count != 1)
575 buf->printf(".%u", *count);
576 buf->writeByte(')');
577}
578
579/******************************* emitComment **********************************/
580
581/** Get leading indentation from 'src' which represents lines of code. */
582static size_t getCodeIndent(const char *src)
583{
584 while (src && (*src == '\r' || *src == '\n'))
585 ++src; // skip until we find the first non-empty line
586
587 size_t codeIndent = 0;
588 while (src && (*src == ' ' || *src == '\t'))
589 {
590 codeIndent++;
591 src++;
592 }
593 return codeIndent;
594}
595
596/** Recursively expand template mixin member docs into the scope. */
597static void expandTemplateMixinComments(TemplateMixin *tm, OutBuffer *buf, Scope *sc)
598{
599 if (!tm->semanticRun) tm->semantic(sc);
600 TemplateDeclaration *td = (tm && tm->tempdecl) ?
601 tm->tempdecl->isTemplateDeclaration() : NULL;
602 if (td && td->members)
603 {
604 for (size_t i = 0; i < td->members->dim; i++)
605 {
606 Dsymbol *sm = (*td->members)[i];
607 TemplateMixin *tmc = sm->isTemplateMixin();
608 if (tmc && tmc->comment)
609 expandTemplateMixinComments(tmc, buf, sc);
610 else
611 emitComment(sm, buf, sc);
612 }
613 }
614}
615
616void emitMemberComments(ScopeDsymbol *sds, OutBuffer *buf, Scope *sc)
617{
618 if (!sds->members)
619 return;
620
621 //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
622
623 const char *m = "$(DDOC_MEMBERS ";
624 if (sds->isTemplateDeclaration())
625 m = "$(DDOC_TEMPLATE_MEMBERS ";
626 else if (sds->isClassDeclaration())
627 m = "$(DDOC_CLASS_MEMBERS ";
628 else if (sds->isStructDeclaration())
629 m = "$(DDOC_STRUCT_MEMBERS ";
630 else if (sds->isEnumDeclaration())
631 m = "$(DDOC_ENUM_MEMBERS ";
632 else if (sds->isModule())
633 m = "$(DDOC_MODULE_MEMBERS ";
634
635 size_t offset1 = buf->offset; // save starting offset
636 buf->writestring(m);
637 size_t offset2 = buf->offset; // to see if we write anything
638
639 sc = sc->push(sds);
640
641 for (size_t i = 0; i < sds->members->dim; i++)
642 {
643 Dsymbol *s = (*sds->members)[i];
644 //printf("\ts = '%s'\n", s->toChars());
645
646 // only expand if parent is a non-template (semantic won't work)
647 if (s->comment && s->isTemplateMixin() && s->parent && !s->parent->isTemplateDeclaration())
648 expandTemplateMixinComments((TemplateMixin *)s, buf, sc);
649
650 emitComment(s, buf, sc);
651 }
652 emitComment(NULL, buf, sc);
653
654 sc->pop();
655
656 if (buf->offset == offset2)
657 {
658 /* Didn't write out any members, so back out last write
659 */
660 buf->offset = offset1;
661 }
662 else
663 buf->writestring(")\n");
664}
665
666void emitProtection(OutBuffer *buf, Prot prot)
667{
668 if (prot.kind != PROTundefined && prot.kind != PROTpublic)
669 {
670 protectionToBuffer(buf, prot);
671 buf->writeByte(' ');
672 }
673}
674
675void emitComment(Dsymbol *s, OutBuffer *buf, Scope *sc)
676{
677 class EmitComment : public Visitor
678 {
679 public:
680 OutBuffer *buf;
681 Scope *sc;
682
683 EmitComment(OutBuffer *buf, Scope *sc)
684 : buf(buf), sc(sc)
685 {
686 }
687
688 void visit(Dsymbol *) {}
689 void visit(InvariantDeclaration *) {}
690 void visit(UnitTestDeclaration *) {}
691 void visit(PostBlitDeclaration *) {}
692 void visit(DtorDeclaration *) {}
693 void visit(StaticCtorDeclaration *) {}
694 void visit(StaticDtorDeclaration *) {}
695 void visit(TypeInfoDeclaration *) {}
696
697 void emit(Scope *sc, Dsymbol *s, const utf8_t *com)
698 {
699 if (s && sc->lastdc && isDitto(com))
700 {
701 sc->lastdc->a.push(s);
702 return;
703 }
704
705 // Put previous doc comment if exists
706 if (DocComment *dc = sc->lastdc)
707 {
708 // Put the declaration signatures as the document 'title'
709 buf->writestring(ddoc_decl_s);
710 for (size_t i = 0; i < dc->a.dim; i++)
711 {
712 Dsymbol *sx = dc->a[i];
713
714 if (i == 0)
715 {
716 size_t o = buf->offset;
717 toDocBuffer(sx, buf, sc);
718 highlightCode(sc, sx, buf, o);
719 continue;
720 }
721
722 buf->writestring("$(DDOC_DITTO ");
723 {
724 size_t o = buf->offset;
725 toDocBuffer(sx, buf, sc);
726 highlightCode(sc, sx, buf, o);
727 }
728 buf->writeByte(')');
729 }
730 buf->writestring(ddoc_decl_e);
731
732 // Put the ddoc comment as the document 'description'
733 buf->writestring(ddoc_decl_dd_s);
734 {
735 dc->writeSections(sc, &dc->a, buf);
736 if (ScopeDsymbol *sds = dc->a[0]->isScopeDsymbol())
737 emitMemberComments(sds, buf, sc);
738 }
739 buf->writestring(ddoc_decl_dd_e);
740 //printf("buf.2 = [[%.*s]]\n", buf->offset - o0, buf->data + o0);
741 }
742
743 if (s)
744 {
745 DocComment *dc = DocComment::parse(s, com);
746 dc->pmacrotable = &sc->_module->macrotable;
747 sc->lastdc = dc;
748 }
749 }
750
751 void visit(Declaration *d)
752 {
753 //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d->toChars(), d->comment);
754 //printf("type = %p\n", d->type);
755 const utf8_t *com = d->comment;
756 if (TemplateDeclaration *td = getEponymousParent(d))
757 {
758 if (isDitto(td->comment))
759 com = td->comment;
760 else
761 com = Lexer::combineComments(td->comment, com);
762 }
763 else
764 {
765 if (!d->ident)
766 return;
767 if (!d->type && !d->isCtorDeclaration() && !d->isAliasDeclaration())
768 return;
769 if (d->protection.kind == PROTprivate || sc->protection.kind == PROTprivate)
770 return;
771 }
772 if (!com)
773 return;
774
775 emit(sc, d, com);
776 }
777
778 void visit(AggregateDeclaration *ad)
779 {
780 //printf("AggregateDeclaration::emitComment() '%s'\n", ad->toChars());
781 const utf8_t *com = ad->comment;
782 if (TemplateDeclaration *td = getEponymousParent(ad))
783 {
784 if (isDitto(td->comment))
785 com = td->comment;
786 else
787 com = Lexer::combineComments(td->comment, com);
788 }
789 else
790 {
791 if (ad->prot().kind == PROTprivate || sc->protection.kind == PROTprivate)
792 return;
793 if (!ad->comment)
794 return;
795 }
796 if (!com)
797 return;
798
799 emit(sc, ad, com);
800 }
801
802 void visit(TemplateDeclaration *td)
803 {
804 //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td->toChars(), td->kind());
805 if (td->prot().kind == PROTprivate || sc->protection.kind == PROTprivate)
806 return;
807 if (!td->comment)
808 return;
809
810 if (Dsymbol *ss = getEponymousMember(td))
811 {
812 ss->accept(this);
813 return;
814 }
815 emit(sc, td, td->comment);
816 }
817
818 void visit(EnumDeclaration *ed)
819 {
820 if (ed->prot().kind == PROTprivate || sc->protection.kind == PROTprivate)
821 return;
822 if (ed->isAnonymous() && ed->members)
823 {
824 for (size_t i = 0; i < ed->members->dim; i++)
825 {
826 Dsymbol *s = (*ed->members)[i];
827 emitComment(s, buf, sc);
828 }
829 return;
830 }
831 if (!ed->comment)
832 return;
833 if (ed->isAnonymous())
834 return;
835
836 emit(sc, ed, ed->comment);
837 }
838
839 void visit(EnumMember *em)
840 {
841 //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em->toChars(), em->comment);
842 if (em->prot().kind == PROTprivate || sc->protection.kind == PROTprivate)
843 return;
844 if (!em->comment)
845 return;
846
847 emit(sc, em, em->comment);
848 }
849
850 void visit(AttribDeclaration *ad)
851 {
852 //printf("AttribDeclaration::emitComment(sc = %p)\n", sc);
853
854 /* A general problem with this, illustrated by BUGZILLA 2516,
855 * is that attributes are not transmitted through to the underlying
856 * member declarations for template bodies, because semantic analysis
857 * is not done for template declaration bodies
858 * (only template instantiations).
859 * Hence, Ddoc omits attributes from template members.
860 */
861
862 Dsymbols *d = ad->include(NULL, NULL);
863
864 if (d)
865 {
866 for (size_t i = 0; i < d->dim; i++)
867 {
868 Dsymbol *s = (*d)[i];
869 //printf("AttribDeclaration::emitComment %s\n", s->toChars());
870 emitComment(s, buf, sc);
871 }
872 }
873 }
874
875 void visit(ProtDeclaration *pd)
876 {
877 if (pd->decl)
878 {
879 Scope *scx = sc;
880 sc = sc->copy();
881 sc->protection = pd->protection;
882 visit((AttribDeclaration *)pd);
883 scx->lastdc = sc->lastdc;
884 sc = sc->pop();
885 }
886 }
887
888 void visit(ConditionalDeclaration *cd)
889 {
890 //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc);
891 if (cd->condition->inc)
892 {
893 visit((AttribDeclaration *)cd);
894 return;
895 }
896
897 /* If generating doc comment, be careful because if we're inside
898 * a template, then include(NULL, NULL) will fail.
899 */
900 Dsymbols *d = cd->decl ? cd->decl : cd->elsedecl;
901 for (size_t i = 0; i < d->dim; i++)
902 {
903 Dsymbol *s = (*d)[i];
904 emitComment(s, buf, sc);
905 }
906 }
907 };
908
909 EmitComment v(buf, sc);
910
911 if (!s)
912 v.emit(sc, NULL, NULL);
913 else
914 s->accept(&v);
915}
916
917/******************************* toDocBuffer **********************************/
918
919void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc)
920{
921 class ToDocBuffer : public Visitor
922 {
923 public:
924 OutBuffer *buf;
925 Scope *sc;
926
927 ToDocBuffer(OutBuffer *buf, Scope *sc)
928 : buf(buf), sc(sc)
929 {
930 }
931
932 void visit(Dsymbol *s)
933 {
934 //printf("Dsymbol::toDocbuffer() %s\n", s->toChars());
935 HdrGenState hgs;
936 hgs.ddoc = true;
937 ::toCBuffer(s, buf, &hgs);
938 }
939
940 void prefix(Dsymbol *s)
941 {
942 if (s->isDeprecated())
943 buf->writestring("deprecated ");
944
945 if (Declaration *d = s->isDeclaration())
946 {
947 emitProtection(buf, d->protection);
948
949 if (d->isStatic())
950 buf->writestring("static ");
951 else if (d->isFinal())
952 buf->writestring("final ");
953 else if (d->isAbstract())
954 buf->writestring("abstract ");
955
956 if (!d->isFuncDeclaration()) // functionToBufferFull handles this
957 {
958 if (d->isConst())
959 buf->writestring("const ");
960 if (d->isImmutable())
961 buf->writestring("immutable ");
962 if (d->isSynchronized())
963 buf->writestring("synchronized ");
964
965 if (d->storage_class & STCmanifest)
966 buf->writestring("enum ");
967 }
968 }
969 }
970
971 void visit(Declaration *d)
972 {
973 if (!d->ident)
974 return;
975
976 TemplateDeclaration *td = getEponymousParent(d);
977 //printf("Declaration::toDocbuffer() %s, originalType = %s, td = %s\n", d->toChars(), d->originalType ? d->originalType->toChars() : "--", td ? td->toChars() : "--");
978
979 HdrGenState hgs;
980 hgs.ddoc = true;
981
982 if (d->isDeprecated())
983 buf->writestring("$(DEPRECATED ");
984
985 prefix(d);
986
987 if (d->type)
988 {
989 Type *origType = d->originalType ? d->originalType : d->type;
990 if (origType->ty == Tfunction)
991 {
992 functionToBufferFull((TypeFunction *)origType, buf, d->ident, &hgs, td);
993 }
994 else
995 ::toCBuffer(origType, buf, d->ident, &hgs);
996 }
997 else
998 buf->writestring(d->ident->toChars());
999
1000 if (d->isVarDeclaration() && td)
1001 {
1002 buf->writeByte('(');
1003 if (td->origParameters && td->origParameters->dim)
1004 {
1005 for (size_t i = 0; i < td->origParameters->dim; i++)
1006 {
1007 if (i)
1008 buf->writestring(", ");
1009 toCBuffer((*td->origParameters)[i], buf, &hgs);
1010 }
1011 }
1012 buf->writeByte(')');
1013 }
1014
1015 // emit constraints if declaration is a templated declaration
1016 if (td && td->constraint)
1017 {
1018 buf->writestring(" if (");
1019 ::toCBuffer(td->constraint, buf, &hgs);
1020 buf->writeByte(')');
1021 }
1022
1023 if (d->isDeprecated())
1024 buf->writestring(")");
1025
1026 buf->writestring(";\n");
1027 }
1028
1029 void visit(AliasDeclaration *ad)
1030 {
1031 //printf("AliasDeclaration::toDocbuffer() %s\n", ad->toChars());
1032 if (!ad->ident)
1033 return;
1034
1035 if (ad->isDeprecated())
1036 buf->writestring("deprecated ");
1037
1038 emitProtection(buf, ad->protection);
1039 buf->printf("alias %s = ", ad->toChars());
1040
1041 if (Dsymbol *s = ad->aliassym) // ident alias
1042 {
1043 prettyPrintDsymbol(s, ad->parent);
1044 }
1045 else if (Type *type = ad->getType()) // type alias
1046 {
1047 if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum)
1048 {
1049 if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type
1050 prettyPrintDsymbol(s, ad->parent);
1051 else
1052 buf->writestring(type->toChars());
1053 }
1054 else
1055 {
1056 // simple type
1057 buf->writestring(type->toChars());
1058 }
1059 }
1060
1061 buf->writestring(";\n");
1062 }
1063
1064 void parentToBuffer(Dsymbol *s)
1065 {
1066 if (s && !s->isPackage() && !s->isModule())
1067 {
1068 parentToBuffer(s->parent);
1069 buf->writestring(s->toChars());
1070 buf->writestring(".");
1071 }
1072 }
1073
1074 static bool inSameModule(Dsymbol *s, Dsymbol *p)
1075 {
1076 for ( ; s ; s = s->parent)
1077 {
1078 if (s->isModule())
1079 break;
1080 }
1081
1082 for ( ; p ; p = p->parent)
1083 {
1084 if (p->isModule())
1085 break;
1086 }
1087
1088 return s == p;
1089 }
1090
1091 void prettyPrintDsymbol(Dsymbol *s, Dsymbol *parent)
1092 {
1093 if (s->parent && (s->parent == parent)) // in current scope -> naked name
1094 {
1095 buf->writestring(s->toChars());
1096 }
1097 else if (!inSameModule(s, parent)) // in another module -> full name
1098 {
1099 buf->writestring(s->toPrettyChars());
1100 }
1101 else // nested in a type in this module -> full name w/o module name
1102 {
1103 // if alias is nested in a user-type use module-scope lookup
1104 if (!parent->isModule() && !parent->isPackage())
1105 buf->writestring(".");
1106
1107 parentToBuffer(s->parent);
1108 buf->writestring(s->toChars());
1109 }
1110 }
1111
1112 void visit(AggregateDeclaration *ad)
1113 {
1114 if (!ad->ident)
1115 return;
1116
1117 buf->printf("%s %s", ad->kind(), ad->toChars());
1118 buf->writestring(";\n");
1119 }
1120
1121 void visit(StructDeclaration *sd)
1122 {
1123 //printf("StructDeclaration::toDocbuffer() %s\n", sd->toChars());
1124 if (!sd->ident)
1125 return;
1126
1127 if (TemplateDeclaration *td = getEponymousParent(sd))
1128 {
1129 toDocBuffer(td, buf, sc);
1130 }
1131 else
1132 {
1133 buf->printf("%s %s", sd->kind(), sd->toChars());
1134 }
1135 buf->writestring(";\n");
1136 }
1137
1138 void visit(ClassDeclaration *cd)
1139 {
1140 //printf("ClassDeclaration::toDocbuffer() %s\n", cd->toChars());
1141 if (!cd->ident)
1142 return;
1143
1144 if (TemplateDeclaration *td = getEponymousParent(cd))
1145 {
1146 toDocBuffer(td, buf, sc);
1147 }
1148 else
1149 {
1150 if (!cd->isInterfaceDeclaration() && cd->isAbstract())
1151 buf->writestring("abstract ");
1152 buf->printf("%s %s", cd->kind(), cd->toChars());
1153 }
1154 int any = 0;
1155 for (size_t i = 0; i < cd->baseclasses->dim; i++)
1156 {
1157 BaseClass *bc = (*cd->baseclasses)[i];
1158
1159 if (bc->sym && bc->sym->ident == Id::Object)
1160 continue;
1161
1162 if (any)
1163 buf->writestring(", ");
1164 else
1165 {
1166 buf->writestring(": ");
1167 any = 1;
1168 }
1169 emitProtection(buf, Prot(PROTpublic));
1170 if (bc->sym)
1171 {
1172 buf->printf("$(DDOC_PSUPER_SYMBOL %s)", bc->sym->toPrettyChars());
1173 }
1174 else
1175 {
1176 HdrGenState hgs;
1177 ::toCBuffer(bc->type, buf, NULL, &hgs);
1178 }
1179 }
1180 buf->writestring(";\n");
1181 }
1182
1183 void visit(EnumDeclaration *ed)
1184 {
1185 if (!ed->ident)
1186 return;
1187
1188 buf->printf("%s %s", ed->kind(), ed->toChars());
1189 if (ed->memtype)
1190 {
1191 buf->writestring(": $(DDOC_ENUM_BASETYPE ");
1192 HdrGenState hgs;
1193 ::toCBuffer(ed->memtype, buf, NULL, &hgs);
1194 buf->writestring(")");
1195 }
1196 buf->writestring(";\n");
1197 }
1198
1199 void visit(EnumMember *em)
1200 {
1201 if (!em->ident)
1202 return;
1203
1204 buf->writestring(em->toChars());
1205 }
1206 };
1207
1208 ToDocBuffer v(buf, sc);
1209 s->accept(&v);
1210}
1211
1212/********************************* DocComment *********************************/
1213
1214DocComment *DocComment::parse(Dsymbol *s, const utf8_t *comment)
1215{
1216 //printf("parse(%s): '%s'\n", s->toChars(), comment);
1217 DocComment *dc = new DocComment();
1218 dc->a.push(s);
1219 if (!comment)
1220 return dc;
1221
1222 dc->parseSections(comment);
1223
1224 for (size_t i = 0; i < dc->sections.dim; i++)
1225 {
1226 Section *sec = dc->sections[i];
1227
1228 if (icmp("copyright", sec->name, sec->namelen) == 0)
1229 {
1230 dc->copyright = sec;
1231 }
1232 if (icmp("macros", sec->name, sec->namelen) == 0)
1233 {
1234 dc->macros = sec;
1235 }
1236 }
1237
1238 return dc;
1239}
1240
1241/*****************************************
1242 * Parse next paragraph out of *pcomment.
1243 * Update *pcomment to point past paragraph.
1244 * Returns NULL if no more paragraphs.
1245 * If paragraph ends in 'identifier:',
1246 * then (*pcomment)[0 .. idlen] is the identifier.
1247 */
1248
1249void DocComment::parseSections(const utf8_t *comment)
1250{
1251 const utf8_t *p;
1252 const utf8_t *pstart;
1253 const utf8_t *pend;
1254 const utf8_t *idstart = NULL; // dead-store to prevent spurious warning
1255 size_t idlen;
1256
1257 const utf8_t *name = NULL;
1258 size_t namelen = 0;
1259
1260 //printf("parseSections('%s')\n", comment);
1261 p = comment;
1262 while (*p)
1263 {
1264 const utf8_t *pstart0 = p;
1265 p = skipwhitespace(p);
1266 pstart = p;
1267 pend = p;
1268
1269 /* Find end of section, which is ended by one of:
1270 * 'identifier:' (but not inside a code section)
1271 * '\0'
1272 */
1273 idlen = 0;
1274 int inCode = 0;
1275 while (1)
1276 {
1277 // Check for start/end of a code section
1278 if (*p == '-')
1279 {
1280 if (!inCode)
1281 {
1282 // restore leading indentation
1283 while (pstart0 < pstart && isIndentWS(pstart-1)) --pstart;
1284 }
1285
1286 int numdash = 0;
1287 while (*p == '-')
1288 {
1289 ++numdash;
1290 p++;
1291 }
1292 // BUG: handle UTF PS and LS too
1293 if ((!*p || *p == '\r' || *p == '\n') && numdash >= 3)
1294 inCode ^= 1;
1295 pend = p;
1296 }
1297
1298 if (!inCode && isIdStart(p))
1299 {
1300 const utf8_t *q = p + utfStride(p);
1301 while (isIdTail(q))
1302 q += utfStride(q);
1303 // Detected tag ends it
1304 if (*q == ':' && isupper(*p)
1305 && (isspace(q[1]) || q[1] == 0))
1306 {
1307 idlen = q - p;
1308 idstart = p;
1309 for (pend = p; pend > pstart; pend--)
1310 {
1311 if (pend[-1] == '\n')
1312 break;
1313 }
1314 p = q + 1;
1315 break;
1316 }
1317 }
1318 while (1)
1319 {
1320 if (!*p)
1321 goto L1;
1322 if (*p == '\n')
1323 {
1324 p++;
1325 if (*p == '\n' && !summary && !namelen && !inCode)
1326 {
1327 pend = p;
1328 p++;
1329 goto L1;
1330 }
1331 break;
1332 }
1333 p++;
1334 pend = p;
1335 }
1336 p = skipwhitespace(p);
1337 }
1338 L1:
1339
1340 if (namelen || pstart < pend)
1341 {
1342 Section *s;
1343 if (icmp("Params", name, namelen) == 0)
1344 s = new ParamSection();
1345 else if (icmp("Macros", name, namelen) == 0)
1346 s = new MacroSection();
1347 else
1348 s = new Section();
1349 s->name = name;
1350 s->namelen = namelen;
1351 s->body = pstart;
1352 s->bodylen = pend - pstart;
1353 s->nooutput = 0;
1354
1355 //printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
1356
1357 sections.push(s);
1358
1359 if (!summary && !namelen)
1360 summary = s;
1361 }
1362
1363 if (idlen)
1364 {
1365 name = idstart;
1366 namelen = idlen;
1367 }
1368 else
1369 {
1370 name = NULL;
1371 namelen = 0;
1372 if (!*p)
1373 break;
1374 }
1375 }
1376}
1377
1378void DocComment::writeSections(Scope *sc, Dsymbols *a, OutBuffer *buf)
1379{
1380 assert(a->dim);
1381
1382 //printf("DocComment::writeSections()\n");
1383 Loc loc = (*a)[0]->loc;
1384 if (Module *m = (*a)[0]->isModule())
1385 {
1386 if (m->md)
1387 loc = m->md->loc;
1388 }
1389
1390 size_t offset1 = buf->offset;
1391 buf->writestring("$(DDOC_SECTIONS ");
1392 size_t offset2 = buf->offset;
1393
1394 for (size_t i = 0; i < sections.dim; i++)
1395 {
1396 Section *sec = sections[i];
1397 if (sec->nooutput)
1398 continue;
1399
1400 //printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
1401 if (!sec->namelen && i == 0)
1402 {
1403 buf->writestring("$(DDOC_SUMMARY ");
1404 size_t o = buf->offset;
1405 buf->write(sec->body, sec->bodylen);
1406 escapeStrayParenthesis(loc, buf, o);
1407 highlightText(sc, a, buf, o);
1408 buf->writestring(")\n");
1409 }
1410 else
1411 sec->write(loc, this, sc, a, buf);
1412 }
1413
1414 for (size_t i = 0; i < a->dim; i++)
1415 {
1416 Dsymbol *s = (*a)[i];
1417 if (Dsymbol *td = getEponymousParent(s))
1418 s = td;
1419
1420 for (UnitTestDeclaration *utd = s->ddocUnittest; utd; utd = utd->ddocUnittest)
1421 {
1422 if (utd->protection.kind == PROTprivate || !utd->comment || !utd->fbody)
1423 continue;
1424
1425 // Strip whitespaces to avoid showing empty summary
1426 const utf8_t *c = utd->comment;
1427 while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r') ++c;
1428
1429 buf->writestring("$(DDOC_EXAMPLES ");
1430
1431 size_t o = buf->offset;
1432 buf->writestring((const char *)c);
1433
1434 if (utd->codedoc)
1435 {
1436 size_t n = getCodeIndent(utd->codedoc);
1437 while (n--) buf->writeByte(' ');
1438 buf->writestring("----\n");
1439 buf->writestring(utd->codedoc);
1440 buf->writestring("----\n");
1441 highlightText(sc, a, buf, o);
1442 }
1443
1444 buf->writestring(")");
1445 }
1446 }
1447
1448 if (buf->offset == offset2)
1449 {
1450 /* Didn't write out any sections, so back out last write
1451 */
1452 buf->offset = offset1;
1453 buf->writestring("$(DDOC_BLANKLINE)\n");
1454 }
1455 else
1456 buf->writestring(")\n");
1457}
1458
1459/***************************************************
1460 */
1461
1462void Section::write(Loc loc, DocComment *, Scope *sc, Dsymbols *a, OutBuffer *buf)
1463{
1464 assert(a->dim);
1465
1466 if (namelen)
1467 {
1468 static const char *table[] =
1469 {
1470 "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
1471 "DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
1472 "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
1473 "VERSION", NULL
1474 };
1475
1476 for (size_t i = 0; table[i]; i++)
1477 {
1478 if (icmp(table[i], name, namelen) == 0)
1479 {
1480 buf->printf("$(DDOC_%s ", table[i]);
1481 goto L1;
1482 }
1483 }
1484
1485 buf->writestring("$(DDOC_SECTION ");
1486
1487 // Replace _ characters with spaces
1488 buf->writestring("$(DDOC_SECTION_H ");
1489 size_t o = buf->offset;
1490 for (size_t u = 0; u < namelen; u++)
1491 {
1492 utf8_t c = name[u];
1493 buf->writeByte((c == '_') ? ' ' : c);
1494 }
1495 escapeStrayParenthesis(loc, buf, o);
1496 buf->writestring(":)\n");
1497 }
1498 else
1499 {
1500 buf->writestring("$(DDOC_DESCRIPTION ");
1501 }
1502 L1:
1503 size_t o = buf->offset;
1504 buf->write(body, bodylen);
1505 escapeStrayParenthesis(loc, buf, o);
1506 highlightText(sc, a, buf, o);
1507 buf->writestring(")\n");
1508}
1509
1510/***************************************************
1511 */
1512
1513void ParamSection::write(Loc loc, DocComment *, Scope *sc, Dsymbols *a, OutBuffer *buf)
1514{
1515 assert(a->dim);
1516 Dsymbol *s = (*a)[0]; // test
1517
1518 const utf8_t *p = body;
1519 size_t len = bodylen;
1520 const utf8_t *pend = p + len;
1521
1522 const utf8_t *tempstart = NULL;
1523 size_t templen = 0;
1524
1525 const utf8_t *namestart = NULL;
1526 size_t namelen = 0; // !=0 if line continuation
1527
1528 const utf8_t *textstart = NULL;
1529 size_t textlen = 0;
1530
1531 size_t paramcount = 0;
1532
1533 buf->writestring("$(DDOC_PARAMS ");
1534 while (p < pend)
1535 {
1536 // Skip to start of macro
1537 while (1)
1538 {
1539 switch (*p)
1540 {
1541 case ' ':
1542 case '\t':
1543 p++;
1544 continue;
1545
1546 case '\n':
1547 p++;
1548 goto Lcont;
1549
1550 default:
1551 if (isIdStart(p) || isCVariadicArg(p, pend - p))
1552 break;
1553 if (namelen)
1554 goto Ltext; // continuation of prev macro
1555 goto Lskipline;
1556 }
1557 break;
1558 }
1559 tempstart = p;
1560
1561 while (isIdTail(p))
1562 p += utfStride(p);
1563 if (isCVariadicArg(p, pend - p))
1564 p += 3;
1565
1566 templen = p - tempstart;
1567
1568 while (*p == ' ' || *p == '\t')
1569 p++;
1570
1571 if (*p != '=')
1572 {
1573 if (namelen)
1574 goto Ltext; // continuation of prev macro
1575 goto Lskipline;
1576 }
1577 p++;
1578
1579 if (namelen)
1580 {
1581 // Output existing param
1582
1583 L1:
1584 //printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1585 ++paramcount;
1586 HdrGenState hgs;
1587 buf->writestring("$(DDOC_PARAM_ROW ");
1588 {
1589 buf->writestring("$(DDOC_PARAM_ID ");
1590 {
1591 size_t o = buf->offset;
1592 Parameter *fparam = isFunctionParameter(a, namestart, namelen);
1593 bool isCVariadic = isCVariadicParameter(a, namestart, namelen);
1594 if (isCVariadic)
1595 {
1596 buf->writestring("...");
1597 }
1598 else if (fparam && fparam->type && fparam->ident)
1599 {
1600 ::toCBuffer(fparam->type, buf, fparam->ident, &hgs);
1601 }
1602 else
1603 {
1604 if (isTemplateParameter(a, namestart, namelen))
1605 {
1606 // 10236: Don't count template parameters for params check
1607 --paramcount;
1608 }
1609 else if (!fparam)
1610 {
1611 warning(s->loc, "Ddoc: function declaration has no parameter '%.*s'", (int)namelen, namestart);
1612 }
1613 buf->write(namestart, namelen);
1614 }
1615 escapeStrayParenthesis(loc, buf, o);
1616 highlightCode(sc, a, buf, o);
1617 }
1618 buf->writestring(")\n");
1619
1620 buf->writestring("$(DDOC_PARAM_DESC ");
1621 {
1622 size_t o = buf->offset;
1623 buf->write(textstart, textlen);
1624 escapeStrayParenthesis(loc, buf, o);
1625 highlightText(sc, a, buf, o);
1626 }
1627 buf->writestring(")");
1628 }
1629 buf->writestring(")\n");
1630 namelen = 0;
1631 if (p >= pend)
1632 break;
1633 }
1634
1635 namestart = tempstart;
1636 namelen = templen;
1637
1638 while (*p == ' ' || *p == '\t')
1639 p++;
1640 textstart = p;
1641
1642 Ltext:
1643 while (*p != '\n')
1644 p++;
1645 textlen = p - textstart;
1646 p++;
1647
1648 Lcont:
1649 continue;
1650
1651 Lskipline:
1652 // Ignore this line
1653 while (*p++ != '\n')
1654 ;
1655 }
1656 if (namelen)
1657 goto L1; // write out last one
1658 buf->writestring(")\n");
1659
1660 TypeFunction *tf = a->dim == 1 ? isTypeFunction(s) : NULL;
1661 if (tf)
1662 {
1663 size_t pcount = (tf->parameters ? tf->parameters->dim : 0) + (int)(tf->varargs == 1);
1664 if (pcount != paramcount)
1665 {
1666 warning(s->loc, "Ddoc: parameter count mismatch");
1667 }
1668 }
1669}
1670
1671/***************************************************
1672 */
1673
1674void MacroSection::write(Loc, DocComment *dc, Scope *, Dsymbols *, OutBuffer *)
1675{
1676 //printf("MacroSection::write()\n");
1677 DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen);
1678}
1679
1680/************************************************
1681 * Parse macros out of Macros: section.
1682 * Macros are of the form:
1683 * name1 = value1
1684 *
1685 * name2 = value2
1686 */
1687
1688void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, const utf8_t *m, size_t mlen)
1689{
1690 const utf8_t *p = m;
1691 size_t len = mlen;
1692 const utf8_t *pend = p + len;
1693
1694 const utf8_t *tempstart = NULL;
1695 size_t templen = 0;
1696
1697 const utf8_t *namestart = NULL;
1698 size_t namelen = 0; // !=0 if line continuation
1699
1700 const utf8_t *textstart = NULL;
1701 size_t textlen = 0;
1702
1703 while (p < pend)
1704 {
1705 // Skip to start of macro
1706 while (1)
1707 {
1708 if (p >= pend)
1709 goto Ldone;
1710 switch (*p)
1711 {
1712 case ' ':
1713 case '\t':
1714 p++;
1715 continue;
1716
1717 case '\r':
1718 case '\n':
1719 p++;
1720 goto Lcont;
1721
1722 default:
1723 if (isIdStart(p))
1724 break;
1725 if (namelen)
1726 goto Ltext; // continuation of prev macro
1727 goto Lskipline;
1728 }
1729 break;
1730 }
1731 tempstart = p;
1732
1733 while (1)
1734 {
1735 if (p >= pend)
1736 goto Ldone;
1737 if (!isIdTail(p))
1738 break;
1739 p += utfStride(p);
1740 }
1741 templen = p - tempstart;
1742
1743 while (1)
1744 {
1745 if (p >= pend)
1746 goto Ldone;
1747 if (!(*p == ' ' || *p == '\t'))
1748 break;
1749 p++;
1750 }
1751
1752 if (*p != '=')
1753 {
1754 if (namelen)
1755 goto Ltext; // continuation of prev macro
1756 goto Lskipline;
1757 }
1758 p++;
1759 if (p >= pend)
1760 goto Ldone;
1761
1762 if (namelen)
1763 {
1764 // Output existing macro
1765 L1:
1766 //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
1767 if (icmp("ESCAPES", namestart, namelen) == 0)
1768 parseEscapes(pescapetable, textstart, textlen);
1769 else
1770 Macro::define(pmacrotable, namestart, namelen, textstart, textlen);
1771 namelen = 0;
1772 if (p >= pend)
1773 break;
1774 }
1775
1776 namestart = tempstart;
1777 namelen = templen;
1778
1779 while (p < pend && (*p == ' ' || *p == '\t'))
1780 p++;
1781 textstart = p;
1782
1783 Ltext:
1784 while (p < pend && *p != '\r' && *p != '\n')
1785 p++;
1786 textlen = p - textstart;
1787
1788 p++;
1789 //printf("p = %p, pend = %p\n", p, pend);
1790
1791 Lcont:
1792 continue;
1793
1794 Lskipline:
1795 // Ignore this line
1796 while (p < pend && *p != '\r' && *p != '\n')
1797 p++;
1798 }
1799Ldone:
1800 if (namelen)
1801 goto L1; // write out last one
1802}
1803
1804/**************************************
1805 * Parse escapes of the form:
1806 * /c/string/
1807 * where c is a single character.
1808 * Multiple escapes can be separated
1809 * by whitespace and/or commas.
1810 */
1811
1812void DocComment::parseEscapes(Escape **pescapetable, const utf8_t *textstart, size_t textlen)
1813{
1814 Escape *escapetable = *pescapetable;
1815
1816 if (!escapetable)
1817 {
1818 escapetable = new Escape;
1819 memset(escapetable, 0, sizeof(Escape));
1820 *pescapetable = escapetable;
1821 }
1822 //printf("parseEscapes('%.*s') pescapetable = %p\n", textlen, textstart, pescapetable);
1823 const utf8_t *p = textstart;
1824 const utf8_t *pend = p + textlen;
1825
1826 while (1)
1827 {
1828 while (1)
1829 {
1830 if (p + 4 >= pend)
1831 return;
1832 if (!(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == ','))
1833 break;
1834 p++;
1835 }
1836 if (p[0] != '/' || p[2] != '/')
1837 return;
1838 utf8_t c = p[1];
1839 p += 3;
1840 const utf8_t *start = p;
1841 while (1)
1842 {
1843 if (p >= pend)
1844 return;
1845 if (*p == '/')
1846 break;
1847 p++;
1848 }
1849 size_t len = p - start;
1850 char *s = (char *)memcpy(mem.xmalloc(len + 1), start, len);
1851 s[len] = 0;
1852 escapetable->strings[c] = s;
1853 //printf("\t%c = '%s'\n", c, s);
1854 p++;
1855 }
1856}
1857
1858
1859/******************************************
1860 * Compare 0-terminated string with length terminated string.
1861 * Return < 0, ==0, > 0
1862 */
1863
1864int cmp(const char *stringz, const void *s, size_t slen)
1865{
1866 size_t len1 = strlen(stringz);
1867
1868 if (len1 != slen)
1869 return (int)(len1 - slen);
1870 return memcmp(stringz, s, slen);
1871}
1872
1873int icmp(const char *stringz, const void *s, size_t slen)
1874{
1875 size_t len1 = strlen(stringz);
1876
1877 if (len1 != slen)
1878 return (int)(len1 - slen);
1879 return Port::memicmp(stringz, (const char *)s, slen);
1880}
1881
1882/*****************************************
1883 * Return true if comment consists entirely of "ditto".
1884 */
1885
1886bool isDitto(const utf8_t *comment)
1887{
1888 if (comment)
1889 {
1890 const utf8_t *p = skipwhitespace(comment);
1891
1892 if (Port::memicmp((const char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0)
1893 return true;
1894 }
1895 return false;
1896}
1897
1898/**********************************************
1899 * Skip white space.
1900 */
1901
1902const utf8_t *skipwhitespace(const utf8_t *p)
1903{
1904 for (; 1; p++)
1905 {
1906 switch (*p)
1907 {
1908 case ' ':
1909 case '\t':
1910 case '\n':
1911 continue;
1912 }
1913 break;
1914 }
1915 return p;
1916}
1917
1918
1919/************************************************
1920 * Scan forward to one of:
1921 * start of identifier
1922 * beginning of next line
1923 * end of buf
1924 */
1925
1926size_t skiptoident(OutBuffer *buf, size_t i)
1927{
1928 while (i < buf->offset)
1929 {
1930 dchar_t c;
1931
1932 size_t oi = i;
1933 if (utf_decodeChar((utf8_t *)buf->data, buf->offset, &i, &c))
1934 {
1935 /* Ignore UTF errors, but still consume input
1936 */
1937 break;
1938 }
1939 if (c >= 0x80)
1940 {
1941 if (!isUniAlpha(c))
1942 continue;
1943 }
1944 else if (!(isalpha(c) || c == '_' || c == '\n'))
1945 continue;
1946 i = oi;
1947 break;
1948 }
1949 return i;
1950}
1951
1952/************************************************
1953 * Scan forward past end of identifier.
1954 */
1955
1956size_t skippastident(OutBuffer *buf, size_t i)
1957{
1958 while (i < buf->offset)
1959 {
1960 dchar_t c;
1961
1962 size_t oi = i;
1963 if (utf_decodeChar((utf8_t *)buf->data, buf->offset, &i, &c))
1964 {
1965 /* Ignore UTF errors, but still consume input
1966 */
1967 break;
1968 }
1969 if (c >= 0x80)
1970 {
1971 if (isUniAlpha(c))
1972 continue;
1973 }
1974 else if (isalnum(c) || c == '_')
1975 continue;
1976 i = oi;
1977 break;
1978 }
1979 return i;
1980}
1981
1982
1983/************************************************
1984 * Scan forward past URL starting at i.
1985 * We don't want to highlight parts of a URL.
1986 * Returns:
1987 * i if not a URL
1988 * index just past it if it is a URL
1989 */
1990
1991size_t skippastURL(OutBuffer *buf, size_t i)
1992{
1993 size_t length = buf->offset - i;
1994 utf8_t *p = (utf8_t *)&buf->data[i];
1995 size_t j;
1996 unsigned sawdot = 0;
1997
1998 if (length > 7 && Port::memicmp((char *)p, "http://", 7) == 0)
1999 {
2000 j = 7;
2001 }
2002 else if (length > 8 && Port::memicmp((char *)p, "https://", 8) == 0)
2003 {
2004 j = 8;
2005 }
2006 else
2007 goto Lno;
2008
2009 for (; j < length; j++)
2010 {
2011 utf8_t c = p[j];
2012 if (isalnum(c))
2013 continue;
2014 if (c == '-' || c == '_' || c == '?' ||
2015 c == '=' || c == '%' || c == '&' ||
2016 c == '/' || c == '+' || c == '#' ||
2017 c == '~')
2018 continue;
2019 if (c == '.')
2020 {
2021 sawdot = 1;
2022 continue;
2023 }
2024 break;
2025 }
2026 if (sawdot)
2027 return i + j;
2028
2029Lno:
2030 return i;
2031}
2032
2033
2034/****************************************************
2035 */
2036
2037bool isIdentifier(Dsymbols *a, const utf8_t *p, size_t len)
2038{
2039 for (size_t i = 0; i < a->dim; i++)
2040 {
2041 const char *s = (*a)[i]->ident->toChars();
2042 if (cmp(s, p, len) == 0)
2043 return true;
2044 }
2045 return false;
2046}
2047
2048/****************************************************
2049 */
2050
2051bool isKeyword(utf8_t *p, size_t len)
2052{
2053 static const char *table[] = { "true", "false", "null", NULL };
2054
2055 for (int i = 0; table[i]; i++)
2056 {
2057 if (cmp(table[i], p, len) == 0)
2058 return true;
2059 }
2060 return false;
2061}
2062
2063/****************************************************
2064 */
2065
2066TypeFunction *isTypeFunction(Dsymbol *s)
2067{
2068 FuncDeclaration *f = s->isFuncDeclaration();
2069
2070 /* f->type may be NULL for template members.
2071 */
2072 if (f && f->type)
2073 {
2074 Type *t = f->originalType ? f->originalType : f->type;
2075 if (t->ty == Tfunction)
2076 return (TypeFunction *)t;
2077 }
2078 return NULL;
2079}
2080
2081/****************************************************
2082 */
2083
2084Parameter *isFunctionParameter(Dsymbols *a, const utf8_t *p, size_t len)
2085{
2086 for (size_t i = 0; i < a->dim; i++)
2087 {
2088 TypeFunction *tf = isTypeFunction((*a)[i]);
2089 if (tf && tf->parameters)
2090 {
2091 for (size_t k = 0; k < tf->parameters->dim; k++)
2092 {
2093 Parameter *fparam = (*tf->parameters)[k];
2094 if (fparam->ident && cmp(fparam->ident->toChars(), p, len) == 0)
2095 {
2096 return fparam;
2097 }
2098 }
2099 }
2100 }
2101 return NULL;
2102}
2103
2104/****************************************************
2105 */
2106
2107TemplateParameter *isTemplateParameter(Dsymbols *a, const utf8_t *p, size_t len)
2108{
2109 for (size_t i = 0; i < a->dim; i++)
2110 {
2111 TemplateDeclaration *td = getEponymousParent((*a)[i]);
2112 if (td && td->origParameters)
2113 {
2114 for (size_t k = 0; k < td->origParameters->dim; k++)
2115 {
2116 TemplateParameter *tp = (*td->origParameters)[k];
2117 if (tp->ident && cmp(tp->ident->toChars(), p, len) == 0)
2118 {
2119 return tp;
2120 }
2121 }
2122 }
2123 }
2124 return NULL;
2125}
2126
2127/****************************************************
2128 * Return true if str is a reserved symbol name
2129 * that starts with a double underscore.
2130 */
2131
2132bool isReservedName(utf8_t *str, size_t len)
2133{
2134 static const char *table[] = {
2135 "__ctor", "__dtor", "__postblit", "__invariant", "__unitTest",
2136 "__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result",
2137 "__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp",
2138 "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__",
2139 "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__",
2140 "__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", NULL };
2141
2142 for (int i = 0; table[i]; i++)
2143 {
2144 if (cmp(table[i], str, len) == 0)
2145 return true;
2146 }
2147 return false;
2148}
2149
2150/**************************************************
2151 * Highlight text section.
2152 */
2153
2154void highlightText(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset)
2155{
2156 Dsymbol *s = a->dim ? (*a)[0] : NULL; // test
2157
2158 //printf("highlightText()\n");
2159
2160 int leadingBlank = 1;
2161 int inCode = 0;
2162 int inBacktick = 0;
2163 //int inComment = 0; // in <!-- ... --> comment
2164 size_t iCodeStart = 0; // start of code section
2165 size_t codeIndent = 0;
2166
2167 size_t iLineStart = offset;
2168
2169 for (size_t i = offset; i < buf->offset; i++)
2170 {
2171 utf8_t c = buf->data[i];
2172
2173 Lcont:
2174 switch (c)
2175 {
2176 case ' ':
2177 case '\t':
2178 break;
2179
2180 case '\n':
2181 if (inBacktick)
2182 {
2183 // `inline code` is only valid if contained on a single line
2184 // otherwise, the backticks should be output literally.
2185 //
2186 // This lets things like `output from the linker' display
2187 // unmolested while keeping the feature consistent with GitHub.
2188
2189 inBacktick = false;
2190 inCode = false; // the backtick also assumes we're in code
2191
2192 // Nothing else is necessary since the DDOC_BACKQUOTED macro is
2193 // inserted lazily at the close quote, meaning the rest of the
2194 // text is already OK.
2195 }
2196
2197 if (!sc->_module->isDocFile &&
2198 !inCode && i == iLineStart && i + 1 < buf->offset) // if "\n\n"
2199 {
2200 static const char blankline[] = "$(DDOC_BLANKLINE)\n";
2201
2202 i = buf->insert(i, blankline, strlen(blankline));
2203 }
2204 leadingBlank = 1;
2205 iLineStart = i + 1;
2206 break;
2207
2208 case '<':
2209 {
2210 leadingBlank = 0;
2211 if (inCode)
2212 break;
2213 utf8_t *p = (utf8_t *)&buf->data[i];
2214 const char *se = sc->_module->escapetable->escapeChar('<');
2215 if (se && strcmp(se, "&lt;") == 0)
2216 {
2217 // Generating HTML
2218 // Skip over comments
2219 if (p[1] == '!' && p[2] == '-' && p[3] == '-')
2220 {
2221 size_t j = i + 4;
2222 p += 4;
2223 while (1)
2224 {
2225 if (j == buf->offset)
2226 goto L1;
2227 if (p[0] == '-' && p[1] == '-' && p[2] == '>')
2228 {
2229 i = j + 2; // place on closing '>'
2230 break;
2231 }
2232 j++;
2233 p++;
2234 }
2235 break;
2236 }
2237
2238 // Skip over HTML tag
2239 if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2])))
2240 {
2241 size_t j = i + 2;
2242 p += 2;
2243 while (1)
2244 {
2245 if (j == buf->offset)
2246 break;
2247 if (p[0] == '>')
2248 {
2249 i = j; // place on closing '>'
2250 break;
2251 }
2252 j++;
2253 p++;
2254 }
2255 break;
2256 }
2257 }
2258 L1:
2259 // Replace '<' with '&lt;' character entity
2260 if (se)
2261 {
2262 size_t len = strlen(se);
2263 buf->remove(i, 1);
2264 i = buf->insert(i, se, len);
2265 i--; // point to ';'
2266 }
2267 break;
2268 }
2269 case '>':
2270 {
2271 leadingBlank = 0;
2272 if (inCode)
2273 break;
2274 // Replace '>' with '&gt;' character entity
2275 const char *se = sc->_module->escapetable->escapeChar('>');
2276 if (se)
2277 {
2278 size_t len = strlen(se);
2279 buf->remove(i, 1);
2280 i = buf->insert(i, se, len);
2281 i--; // point to ';'
2282 }
2283 break;
2284 }
2285 case '&':
2286 {
2287 leadingBlank = 0;
2288 if (inCode)
2289 break;
2290 utf8_t *p = (utf8_t *)&buf->data[i];
2291 if (p[1] == '#' || isalpha(p[1]))
2292 break; // already a character entity
2293 // Replace '&' with '&amp;' character entity
2294 const char *se = sc->_module->escapetable->escapeChar('&');
2295 if (se)
2296 {
2297 size_t len = strlen(se);
2298 buf->remove(i, 1);
2299 i = buf->insert(i, se, len);
2300 i--; // point to ';'
2301 }
2302 break;
2303 }
2304 case '`':
2305 {
2306 if (inBacktick)
2307 {
2308 inBacktick = 0;
2309 inCode = 0;
2310
2311 OutBuffer codebuf;
2312
2313 codebuf.write(buf->data + iCodeStart + 1, i - (iCodeStart + 1));
2314
2315 // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
2316 highlightCode(sc, a, &codebuf, 0);
2317
2318 buf->remove(iCodeStart, i - iCodeStart + 1); // also trimming off the current `
2319
2320 static const char pre[] = "$(DDOC_BACKQUOTED ";
2321 i = buf->insert(iCodeStart, pre, strlen(pre));
2322 i = buf->insert(i, (char *)codebuf.data, codebuf.offset);
2323 i = buf->insert(i, ")", 1);
2324
2325 i--; // point to the ending ) so when the for loop does i++, it will see the next character
2326
2327 break;
2328 }
2329
2330 if (inCode)
2331 break;
2332
2333 inCode = 1;
2334 inBacktick = 1;
2335 codeIndent = 0; // inline code is not indented
2336
2337 // All we do here is set the code flags and record
2338 // the location. The macro will be inserted lazily
2339 // so we can easily cancel the inBacktick if we come
2340 // across a newline character.
2341 iCodeStart = i;
2342
2343 break;
2344 }
2345 case '-':
2346 /* A line beginning with --- delimits a code section.
2347 * inCode tells us if it is start or end of a code section.
2348 */
2349 if (leadingBlank)
2350 {
2351 size_t istart = i;
2352 size_t eollen = 0;
2353
2354 leadingBlank = 0;
2355 while (1)
2356 {
2357 ++i;
2358 if (i >= buf->offset)
2359 break;
2360 c = buf->data[i];
2361 if (c == '\n')
2362 {
2363 eollen = 1;
2364 break;
2365 }
2366 if (c == '\r')
2367 {
2368 eollen = 1;
2369 if (i + 1 >= buf->offset)
2370 break;
2371 if (buf->data[i + 1] == '\n')
2372 {
2373 eollen = 2;
2374 break;
2375 }
2376 }
2377 // BUG: handle UTF PS and LS too
2378 if (c != '-')
2379 goto Lcont;
2380 }
2381 if (i - istart < 3)
2382 goto Lcont;
2383
2384 // We have the start/end of a code section
2385
2386 // Remove the entire --- line, including blanks and \n
2387 buf->remove(iLineStart, i - iLineStart + eollen);
2388 i = iLineStart;
2389
2390 if (inCode && (i <= iCodeStart))
2391 {
2392 // Empty code section, just remove it completely.
2393 inCode = 0;
2394 break;
2395 }
2396
2397 if (inCode)
2398 {
2399 inCode = 0;
2400 // The code section is from iCodeStart to i
2401 OutBuffer codebuf;
2402
2403 codebuf.write(buf->data + iCodeStart, i - iCodeStart);
2404 codebuf.writeByte(0);
2405
2406 // Remove leading indentations from all lines
2407 bool lineStart = true;
2408 utf8_t *endp = (utf8_t *)codebuf.data + codebuf.offset;
2409 for (utf8_t *p = (utf8_t *)codebuf.data; p < endp; )
2410 {
2411 if (lineStart)
2412 {
2413 size_t j = codeIndent;
2414 utf8_t *q = p;
2415 while (j-- > 0 && q < endp && isIndentWS(q))
2416 ++q;
2417 codebuf.remove(p - (utf8_t *)codebuf.data, q - p);
2418 assert((utf8_t *)codebuf.data <= p);
2419 assert(p < (utf8_t *)codebuf.data + codebuf.offset);
2420 lineStart = false;
2421 endp = (utf8_t *)codebuf.data + codebuf.offset; // update
2422 continue;
2423 }
2424 if (*p == '\n')
2425 lineStart = true;
2426 ++p;
2427 }
2428
2429 highlightCode2(sc, a, &codebuf, 0);
2430 buf->remove(iCodeStart, i - iCodeStart);
2431 i = buf->insert(iCodeStart, codebuf.data, codebuf.offset);
2432 i = buf->insert(i, (const char *)")\n", 2);
2433 i -= 2; // in next loop, c should be '\n'
2434 }
2435 else
2436 {
2437 static const char d_code[] = "$(D_CODE ";
2438
2439 inCode = 1;
2440 codeIndent = istart - iLineStart; // save indent count
2441 i = buf->insert(i, d_code, strlen(d_code));
2442 iCodeStart = i;
2443 i--; // place i on >
2444 leadingBlank = true;
2445 }
2446 }
2447 break;
2448
2449 default:
2450 leadingBlank = 0;
2451 if (sc->_module->isDocFile || inCode)
2452 break;
2453
2454 utf8_t *start = (utf8_t *)buf->data + i;
2455 if (isIdStart(start))
2456 {
2457 size_t j = skippastident(buf, i);
2458 if (i < j)
2459 {
2460 size_t k = skippastURL(buf, i);
2461 if (i < k)
2462 {
2463 i = k - 1;
2464 break;
2465 }
2466 }
2467 else
2468 break;
2469 size_t len = j - i;
2470
2471 // leading '_' means no highlight unless it's a reserved symbol name
2472 if (c == '_' &&
2473 (i == 0 || !isdigit(*(start - 1))) &&
2474 (i == buf->offset - 1 || !isReservedName(start, len)))
2475 {
2476 buf->remove(i, 1);
2477 i = j - 1;
2478 break;
2479 }
2480 if (isIdentifier(a, start, len))
2481 {
2482 i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
2483 break;
2484 }
2485 if (isKeyword(start, len))
2486 {
2487 i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1;
2488 break;
2489 }
2490 if (isFunctionParameter(a, start, len))
2491 {
2492 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2493 i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
2494 break;
2495 }
2496
2497 i = j - 1;
2498 }
2499 break;
2500 }
2501 }
2502 if (inCode)
2503 error(s ? s->loc : Loc(), "unmatched --- in DDoc comment");
2504}
2505
2506/**************************************************
2507 * Highlight code for DDOC section.
2508 */
2509
2510void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset)
2511{
2512 //printf("highlightCode(s = %s '%s')\n", s->kind(), s->toChars());
2513 OutBuffer ancbuf;
2514 emitAnchor(&ancbuf, s, sc);
2515 buf->insert(offset, (char *)ancbuf.data, ancbuf.offset);
2516 offset += ancbuf.offset;
2517
2518 Dsymbols a;
2519 a.push(s);
2520 highlightCode(sc, &a, buf, offset);
2521}
2522
2523/****************************************************
2524 */
2525
2526void highlightCode(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset)
2527{
2528 //printf("highlightCode(a = '%s')\n", a->toChars());
2529
2530 for (size_t i = offset; i < buf->offset; i++)
2531 {
2532 utf8_t c = buf->data[i];
2533 const char *se = sc->_module->escapetable->escapeChar(c);
2534 if (se)
2535 {
2536 size_t len = strlen(se);
2537 buf->remove(i, 1);
2538 i = buf->insert(i, se, len);
2539 i--; // point to ';'
2540 continue;
2541 }
2542
2543 utf8_t *start = (utf8_t *)buf->data + i;
2544 if (isIdStart(start))
2545 {
2546 size_t j = skippastident(buf, i);
2547 if (i < j)
2548 {
2549 size_t len = j - i;
2550 if (isIdentifier(a, start, len))
2551 {
2552 i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
2553 continue;
2554 }
2555 if (isFunctionParameter(a, start, len))
2556 {
2557 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2558 i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
2559 continue;
2560 }
2561 i = j - 1;
2562 }
2563 }
2564 }
2565}
2566
2567/****************************************
2568 */
2569
2570void highlightCode3(Scope *sc, OutBuffer *buf, const utf8_t *p, const utf8_t *pend)
2571{
2572 for (; p < pend; p++)
2573 {
2574 const char *s = sc->_module->escapetable->escapeChar(*p);
2575 if (s)
2576 buf->writestring(s);
2577 else
2578 buf->writeByte(*p);
2579 }
2580}
2581
2582/**************************************************
2583 * Highlight code for CODE section.
2584 */
2585
2586void highlightCode2(Scope *sc, Dsymbols *a, OutBuffer *buf, size_t offset)
2587{
2588 unsigned errorsave = global.errors;
2589 Lexer lex(NULL, (utf8_t *)buf->data, 0, buf->offset - 1, 0, 1);
2590 OutBuffer res;
2591 const utf8_t *lastp = (utf8_t *)buf->data;
2592
2593 //printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data);
2594 res.reserve(buf->offset);
2595 while (1)
2596 {
2597 Token tok;
2598 lex.scan(&tok);
2599 highlightCode3(sc, &res, lastp, tok.ptr);
2600
2601 const char *highlight = NULL;
2602 switch (tok.value)
2603 {
2604 case TOKidentifier:
2605 {
2606 if (!sc)
2607 break;
2608 size_t len = lex.p - tok.ptr;
2609 if (isIdentifier(a, tok.ptr, len))
2610 {
2611 highlight = "$(D_PSYMBOL ";
2612 break;
2613 }
2614 if (isFunctionParameter(a, tok.ptr, len))
2615 {
2616 //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
2617 highlight = "$(D_PARAM ";
2618 break;
2619 }
2620 break;
2621 }
2622 case TOKcomment:
2623 highlight = "$(D_COMMENT ";
2624 break;
2625
2626 case TOKstring:
2627 highlight = "$(D_STRING ";
2628 break;
2629
2630 default:
2631 if (tok.isKeyword())
2632 highlight = "$(D_KEYWORD ";
2633 break;
2634 }
2635 if (highlight)
2636 {
2637 res.writestring(highlight);
2638 size_t o = res.offset;
2639 highlightCode3(sc, &res, tok.ptr, lex.p);
2640 if (tok.value == TOKcomment || tok.value == TOKstring)
2641 escapeDdocString(&res, o); // Bugzilla 7656, 7715, and 10519
2642 res.writeByte(')');
2643 }
2644 else
2645 highlightCode3(sc, &res, tok.ptr, lex.p);
2646 if (tok.value == TOKeof)
2647 break;
2648 lastp = lex.p;
2649 }
2650 buf->setsize(offset);
2651 buf->write(&res);
2652 global.errors = errorsave;
2653}
2654
2655/***************************************
2656 * Find character string to replace c with.
2657 */
2658
2659const char *Escape::escapeChar(unsigned c)
2660{
2661 assert(c < 256);
2662 //printf("escapeChar('%c') => %p, %p\n", c, strings, strings[c]);
2663 return strings[c];
2664}
2665
2666/****************************************
2667 * Determine if p points to the start of a "..." parameter identifier.
2668 */
2669
2670bool isCVariadicArg(const utf8_t *p, size_t len)
2671{
2672 return len >= 3 && cmp("...", p, 3) == 0;
2673}
2674
2675/****************************************
2676 * Determine if p points to the start of an identifier.
2677 */
2678
2679bool isIdStart(const utf8_t *p)
2680{
2681 unsigned c = *p;
2682 if (isalpha(c) || c == '_')
2683 return true;
2684 if (c >= 0x80)
2685 {
2686 size_t i = 0;
2687 if (utf_decodeChar(p, 4, &i, &c))
2688 return false; // ignore errors
2689 if (isUniAlpha(c))
2690 return true;
2691 }
2692 return false;
2693}
2694
2695/****************************************
2696 * Determine if p points to the rest of an identifier.
2697 */
2698
2699bool isIdTail(const utf8_t *p)
2700{
2701 unsigned c = *p;
2702 if (isalnum(c) || c == '_')
2703 return true;
2704 if (c >= 0x80)
2705 {
2706 size_t i = 0;
2707 if (utf_decodeChar(p, 4, &i, &c))
2708 return false; // ignore errors
2709 if (isUniAlpha(c))
2710 return true;
2711 }
2712 return false;
2713}
2714
2715/****************************************
2716 * Determine if p points to the indentation space.
2717 */
2718
2719bool isIndentWS(const utf8_t *p)
2720{
2721 return (*p == ' ') || (*p == '\t');
2722}
2723
2724/*****************************************
2725 * Return number of bytes in UTF character.
2726 */
2727
2728int utfStride(const utf8_t *p)
2729{
2730 unsigned c = *p;
2731 if (c < 0x80)
2732 return 1;
2733 size_t i = 0;
2734 utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input
2735 return (int)i;
2736}