Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2231 lines (1949 sloc) 60.811 kb
// Compiler implementation of the D programming language
// Copyright (c) 1999-2012 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
// This implements the Ddoc capability.
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <assert.h>
#include "rmem.h"
#include "root.h"
#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__
#include "gnuc.h"
#endif
#include "mars.h"
#include "dsymbol.h"
#include "macro.h"
#include "template.h"
#include "lexer.h"
#include "aggregate.h"
#include "declaration.h"
#include "enum.h"
#include "id.h"
#include "module.h"
#include "scope.h"
#include "hdrgen.h"
#include "doc.h"
#include "mtype.h"
#include "utf.h"
struct Escape
{
const char *strings[256];
static const char *escapeChar(unsigned c);
};
struct Section
{
unsigned char *name;
unsigned namelen;
unsigned char *body;
unsigned bodylen;
int nooutput;
virtual void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
};
struct ParamSection : Section
{
void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
};
struct MacroSection : Section
{
void write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf);
};
typedef ArrayBase<Section> Sections;
struct DocComment
{
Sections sections; // Section*[]
Section *summary;
Section *copyright;
Section *macros;
Macro **pmacrotable;
Escape **pescapetable;
DocComment() :
summary(NULL), copyright(NULL), macros(NULL), pmacrotable(NULL), pescapetable(NULL)
{ }
static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment);
static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen);
static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen);
void parseSections(unsigned char *comment);
void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf);
};
int cmp(const char *stringz, void *s, size_t slen);
int icmp(const char *stringz, void *s, size_t slen);
int isDitto(unsigned char *comment);
unsigned char *skipwhitespace(unsigned char *p);
unsigned skiptoident(OutBuffer *buf, size_t i);
unsigned skippastident(OutBuffer *buf, size_t i);
unsigned skippastURL(OutBuffer *buf, size_t i);
void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset);
Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len);
int isIdStart(unsigned char *p);
int isIdTail(unsigned char *p);
int utfStride(unsigned char *p);
static unsigned char ddoc_default[] = "\
DDOC = <html><head>\n\
<META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
<title>$(TITLE)</title>\n\
</head><body>\n\
<h1>$(TITLE)</h1>\n\
$(BODY)\n\
<hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT))\n\
</body></html>\n\
\n\
B = <b>$0</b>\n\
I = <i>$0</i>\n\
U = <u>$0</u>\n\
P = <p>$0</p>\n\
DL = <dl>$0</dl>\n\
DT = <dt>$0</dt>\n\
DD = <dd>$0</dd>\n\
TABLE = <table>$0</table>\n\
TR = <tr>$0</tr>\n\
TH = <th>$0</th>\n\
TD = <td>$0</td>\n\
OL = <ol>$0</ol>\n\
UL = <ul>$0</ul>\n\
LI = <li>$0</li>\n\
BIG = <big>$0</big>\n\
SMALL = <small>$0</small>\n\
BR = <br>\n\
LINK = <a href=\"$0\">$0</a>\n\
LINK2 = <a href=\"$1\">$+</a>\n\
LPAREN= (\n\
RPAREN= )\n\
DOLLAR= $\n\
\n\
RED = <font color=red>$0</font>\n\
BLUE = <font color=blue>$0</font>\n\
GREEN = <font color=green>$0</font>\n\
YELLOW =<font color=yellow>$0</font>\n\
BLACK = <font color=black>$0</font>\n\
WHITE = <font color=white>$0</font>\n\
\n\
D_CODE = <pre class=\"d_code\">$0</pre>\n\
D_COMMENT = $(GREEN $0)\n\
D_STRING = $(RED $0)\n\
D_KEYWORD = $(BLUE $0)\n\
D_PSYMBOL = $(U $0)\n\
D_PARAM = $(I $0)\n\
\n\
DDOC_COMMENT = <!-- $0 -->\n\
DDOC_DECL = $(DT $(BIG $0))\n\
DDOC_DECL_DD = $(DD $0)\n\
DDOC_DITTO = $(BR)$0\n\
DDOC_SECTIONS = $0\n\
DDOC_SUMMARY = $0$(BR)$(BR)\n\
DDOC_DESCRIPTION = $0$(BR)$(BR)\n\
DDOC_AUTHORS = $(B Authors:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_BUGS = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_DATE = $(B Date:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_EXAMPLES = $(B Examples:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_HISTORY = $(B History:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_LICENSE = $(B License:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_RETURNS = $(B Returns:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_SEE_ALSO = $(B See Also:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_THROWS = $(B Throws:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_VERSION = $(B Version:)$(BR)\n$0$(BR)$(BR)\n\
DDOC_SECTION_H = $(B $0)$(BR)\n\
DDOC_SECTION = $0$(BR)$(BR)\n\
DDOC_MEMBERS = $(DL $0)\n\
DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)\n\
DDOC_CLASS_MEMBERS = $(DDOC_MEMBERS $0)\n\
DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)\n\
DDOC_ENUM_MEMBERS = $(DDOC_MEMBERS $0)\n\
DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)\n\
DDOC_PARAMS = $(B Params:)$(BR)\n$(TABLE $0)$(BR)\n\
DDOC_PARAM_ROW = $(TR $0)\n\
DDOC_PARAM_ID = $(TD $0)\n\
DDOC_PARAM_DESC = $(TD $0)\n\
DDOC_BLANKLINE = $(BR)$(BR)\n\
\n\
DDOC_PSYMBOL = $(U $0)\n\
DDOC_KEYWORD = $(B $0)\n\
DDOC_PARAM = $(I $0)\n\
\n\
ESCAPES = /</&lt;/\n\
/>/&gt;/\n\
/&/&amp;/\n\
";
static char ddoc_decl_s[] = "$(DDOC_DECL ";
static char ddoc_decl_e[] = ")\n";
static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
static char ddoc_decl_dd_e[] = ")\n";
/****************************************************
*/
void Module::gendocfile()
{
static OutBuffer mbuf;
static int mbuf_done;
OutBuffer buf;
//printf("Module::gendocfile()\n");
if (!mbuf_done) // if not already read the ddoc files
{ mbuf_done = 1;
// Use our internal default
mbuf.write(ddoc_default, sizeof(ddoc_default) - 1);
// Override with DDOCFILE specified in the sc.ini file
char *p = getenv("DDOCFILE");
if (p)
global.params.ddocfiles->shift(p);
// Override with the ddoc macro files from the command line
for (size_t i = 0; i < global.params.ddocfiles->dim; i++)
{
FileName f((*global.params.ddocfiles)[i], 0);
File file(&f);
file.readv();
// BUG: convert file contents to UTF-8 before use
//printf("file: '%.*s'\n", file.len, file.buffer);
mbuf.write(file.buffer, file.len);
}
}
DocComment::parseMacros(&escapetable, &macrotable, mbuf.data, mbuf.offset);
Scope *sc = Scope::createGlobal(this); // create root scope
sc->docbuf = &buf;
DocComment *dc = DocComment::parse(sc, this, comment);
dc->pmacrotable = &macrotable;
dc->pescapetable = &escapetable;
// Generate predefined macros
// Set the title to be the name of the module
{ const char *p = toPrettyChars();
Macro::define(&macrotable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p));
}
// Set time macros
{ time_t t;
time(&t);
char *p = ctime(&t);
p = mem.strdup(p);
Macro::define(&macrotable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p));
Macro::define(&macrotable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4);
}
char *srcfilename = srcfile->toChars();
Macro::define(&macrotable, (unsigned char *)"SRCFILENAME", 11, (unsigned char *)srcfilename, strlen(srcfilename));
char *docfilename = docfile->toChars();
Macro::define(&macrotable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename));
if (dc->copyright)
{
dc->copyright->nooutput = 1;
Macro::define(&macrotable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen);
}
buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars());
if (isDocFile)
{
size_t commentlen = strlen((char *)comment);
if (dc->macros)
{
commentlen = dc->macros->name - comment;
dc->macros->write(dc, sc, this, sc->docbuf);
}
sc->docbuf->write(comment, commentlen);
highlightText(NULL, this, sc->docbuf, 0);
}
else
{
dc->writeSections(sc, this, sc->docbuf);
emitMemberComments(sc);
}
//printf("BODY= '%.*s'\n", buf.offset, buf.data);
Macro::define(&macrotable, (unsigned char *)"BODY", 4, buf.data, buf.offset);
OutBuffer buf2;
buf2.writestring("$(DDOC)\n");
unsigned end = buf2.offset;
macrotable->expand(&buf2, 0, &end, NULL, 0);
#if 1
/* Remove all the escape sequences from buf2,
* and make CR-LF the newline.
*/
{
buf.setsize(0);
buf.reserve(buf2.offset);
unsigned char *p = buf2.data;
for (unsigned j = 0; j < buf2.offset; j++)
{
unsigned char c = p[j];
if (c == 0xFF && j + 1 < buf2.offset)
{
j++;
continue;
}
if (c == '\n')
buf.writeByte('\r');
else if (c == '\r')
{
buf.writestring("\r\n");
if (j + 1 < buf2.offset && p[j + 1] == '\n')
{
j++;
}
continue;
}
buf.writeByte(c);
}
}
// Transfer image to file
assert(docfile);
docfile->setbuffer(buf.data, buf.offset);
docfile->ref = 1;
char *pt = FileName::path(docfile->toChars());
if (*pt)
FileName::ensurePathExists(pt);
mem.free(pt);
docfile->writev();
#else
/* Remove all the escape sequences from buf2
*/
{ unsigned i = 0;
unsigned char *p = buf2.data;
for (unsigned j = 0; j < buf2.offset; j++)
{
if (p[j] == 0xFF && j + 1 < buf2.offset)
{
j++;
continue;
}
p[i] = p[j];
i++;
}
buf2.setsize(i);
}
// Transfer image to file
docfile->setbuffer(buf2.data, buf2.offset);
docfile->ref = 1;
char *pt = FileName::path(docfile->toChars());
if (*pt)
FileName::ensurePathExists(pt);
mem.free(pt);
docfile->writev();
#endif
}
/****************************************************
* Having unmatched parentheses can hose the output of Ddoc,
* as the macros depend on properly nested parentheses.
* This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
* to preserve text literally. This also means macros in the
* text won't be expanded.
*/
void escapeDdocString(OutBuffer *buf, unsigned start)
{
for (unsigned u = start; u < buf->offset; u++)
{
unsigned char c = buf->data[u];
switch(c)
{
case '$':
buf->remove(u, 1);
buf->insert(u, "$(DOLLAR)", 9);
u += 8;
break;
case '(':
buf->remove(u, 1); //remove the (
buf->insert(u, "$(LPAREN)", 9); //insert this instead
u += 8; //skip over newly inserted macro
break;
case ')':
buf->remove(u, 1); //remove the )
buf->insert(u, "$(RPAREN)", 9); //insert this instead
u += 8; //skip over newly inserted macro
break;
}
}
}
/****************************************************
* Having unmatched parentheses can hose the output of Ddoc,
* as the macros depend on properly nested parentheses.
* Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
*/
void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc)
{
unsigned par_open = 0;
for (unsigned u = start; u < buf->offset; u++)
{
unsigned char c = buf->data[u];
switch(c)
{
case '(':
par_open++;
break;
case ')':
if (par_open == 0)
{
//stray ')'
if (global.params.warnings)
warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output."
" Use $(RPAREN) instead for unpaired right parentheses.");
buf->remove(u, 1); //remove the )
buf->insert(u, "$(RPAREN)", 9); //insert this instead
u += 8; //skip over newly inserted macro
}
else
par_open--;
break;
#if 0
// For this to work, loc must be set to the beginning of the passed
// text which is currently not possible
// (loc is set to the Loc of the Dsymbol)
case '\n':
loc.linnum++;
break;
#endif
}
}
if (par_open) // if any unmatched lparens
{ par_open = 0;
for (unsigned u = buf->offset; u > start;)
{ u--;
unsigned char c = buf->data[u];
switch(c)
{
case ')':
par_open++;
break;
case '(':
if (par_open == 0)
{
//stray '('
if (global.params.warnings)
warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output."
" Use $(LPAREN) instead for unpaired left parentheses.");
buf->remove(u, 1); //remove the (
buf->insert(u, "$(LPAREN)", 9); //insert this instead
}
else
par_open--;
break;
}
}
}
}
/******************************* emitComment **********************************/
/*
* Emit doc comment to documentation file
*/
void Dsymbol::emitDitto(Scope *sc)
{
//printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars());
OutBuffer *buf = sc->docbuf;
unsigned o;
OutBuffer b;
b.writestring("$(DDOC_DITTO ");
o = b.offset;
toDocBuffer(&b);
//printf("b: '%.*s'\n", b.offset, b.data);
/* If 'this' is a function template, then highlightCode() was
* already run by FuncDeclaration::toDocbuffer().
*/
TemplateDeclaration *td;
if (parent &&
(td = parent->isTemplateDeclaration()) != NULL &&
td->onemember == this)
{
}
else
highlightCode(sc, this, &b, o);
b.writeByte(')');
buf->spread(sc->lastoffset, b.offset);
memcpy(buf->data + sc->lastoffset, b.data, b.offset);
sc->lastoffset += b.offset;
}
void ScopeDsymbol::emitMemberComments(Scope *sc)
{
//printf("ScopeDsymbol::emitMemberComments() %s\n", toChars());
OutBuffer *buf = sc->docbuf;
if (members)
{ const char *m = "$(DDOC_MEMBERS \n";
if (isModule())
m = "$(DDOC_MODULE_MEMBERS \n";
else if (isClassDeclaration())
m = "$(DDOC_CLASS_MEMBERS \n";
else if (isStructDeclaration())
m = "$(DDOC_STRUCT_MEMBERS \n";
else if (isEnumDeclaration())
m = "$(DDOC_ENUM_MEMBERS \n";
else if (isTemplateDeclaration())
m = "$(DDOC_TEMPLATE_MEMBERS \n";
unsigned offset1 = buf->offset; // save starting offset
buf->writestring(m);
unsigned offset2 = buf->offset; // to see if we write anything
sc = sc->push(this);
for (size_t i = 0; i < members->dim; i++)
{
Dsymbol *s = (*members)[i];
//printf("\ts = '%s'\n", s->toChars());
s->emitComment(sc);
}
sc->pop();
if (buf->offset == offset2)
{
/* Didn't write out any members, so back out last write
*/
buf->offset = offset1;
}
else
buf->writestring(")\n");
}
}
void emitProtection(OutBuffer *buf, PROT prot)
{
const char *p;
switch (prot)
{
case PROTpackage: p = "package"; break;
case PROTprotected: p = "protected"; break;
case PROTexport: p = "export"; break;
default: p = NULL; break;
}
if (p)
buf->printf("%s ", p);
}
void Dsymbol::emitComment(Scope *sc) { }
void InvariantDeclaration::emitComment(Scope *sc) { }
#if DMDV2
void PostBlitDeclaration::emitComment(Scope *sc) { }
#endif
void DtorDeclaration::emitComment(Scope *sc) { }
void StaticCtorDeclaration::emitComment(Scope *sc) { }
void StaticDtorDeclaration::emitComment(Scope *sc) { }
void ClassInfoDeclaration::emitComment(Scope *sc) { }
void ModuleInfoDeclaration::emitComment(Scope *sc) { }
void TypeInfoDeclaration::emitComment(Scope *sc) { }
void Declaration::emitComment(Scope *sc)
{
//printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
//printf("type = %p\n", type);
if (protection == PROTprivate || !ident ||
(!type && !isCtorDeclaration() && !isAliasDeclaration()))
return;
if (!comment)
return;
OutBuffer *buf = sc->docbuf;
DocComment *dc = DocComment::parse(sc, this, comment);
unsigned o;
if (!dc)
{
emitDitto(sc);
return;
}
dc->pmacrotable = &sc->module->macrotable;
buf->writestring(ddoc_decl_s);
o = buf->offset;
toDocBuffer(buf);
highlightCode(sc, this, buf, o);
sc->lastoffset = buf->offset;
buf->writestring(ddoc_decl_e);
buf->writestring(ddoc_decl_dd_s);
dc->writeSections(sc, this, buf);
buf->writestring(ddoc_decl_dd_e);
}
void AggregateDeclaration::emitComment(Scope *sc)
{
//printf("AggregateDeclaration::emitComment() '%s'\n", toChars());
if (prot() == PROTprivate)
return;
if (!comment)
return;
OutBuffer *buf = sc->docbuf;
DocComment *dc = DocComment::parse(sc, this, comment);
if (!dc)
{
emitDitto(sc);
return;
}
dc->pmacrotable = &sc->module->macrotable;
buf->writestring(ddoc_decl_s);
toDocBuffer(buf);
sc->lastoffset = buf->offset;
buf->writestring(ddoc_decl_e);
buf->writestring(ddoc_decl_dd_s);
dc->writeSections(sc, this, buf);
emitMemberComments(sc);
buf->writestring(ddoc_decl_dd_e);
}
void TemplateDeclaration::emitComment(Scope *sc)
{
//printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind());
if (prot() == PROTprivate)
return;
unsigned char *com = comment;
int hasmembers = 1;
Dsymbol *ss = this;
if (onemember)
{
ss = onemember->isAggregateDeclaration();
if (!ss)
{
ss = onemember->isFuncDeclaration();
if (ss)
{ hasmembers = 0;
if (com != ss->comment)
com = Lexer::combineComments(com, ss->comment);
}
else
ss = this;
}
}
if (!com)
return;
OutBuffer *buf = sc->docbuf;
DocComment *dc = DocComment::parse(sc, this, com);
unsigned o;
if (!dc)
{
ss->emitDitto(sc);
return;
}
dc->pmacrotable = &sc->module->macrotable;
buf->writestring(ddoc_decl_s);
o = buf->offset;
ss->toDocBuffer(buf);
if (ss == this)
highlightCode(sc, this, buf, o);
sc->lastoffset = buf->offset;
buf->writestring(ddoc_decl_e);
buf->writestring(ddoc_decl_dd_s);
dc->writeSections(sc, this, buf);
if (hasmembers)
((ScopeDsymbol *)ss)->emitMemberComments(sc);
buf->writestring(ddoc_decl_dd_e);
}
void EnumDeclaration::emitComment(Scope *sc)
{
if (prot() == PROTprivate)
return;
// if (!comment)
{ if (isAnonymous() && members)
{
for (size_t i = 0; i < members->dim; i++)
{
Dsymbol *s = (*members)[i];
s->emitComment(sc);
}
return;
}
}
if (!comment)
return;
if (isAnonymous())
return;
OutBuffer *buf = sc->docbuf;
DocComment *dc = DocComment::parse(sc, this, comment);
if (!dc)
{
emitDitto(sc);
return;
}
dc->pmacrotable = &sc->module->macrotable;
buf->writestring(ddoc_decl_s);
toDocBuffer(buf);
sc->lastoffset = buf->offset;
buf->writestring(ddoc_decl_e);
buf->writestring(ddoc_decl_dd_s);
dc->writeSections(sc, this, buf);
emitMemberComments(sc);
buf->writestring(ddoc_decl_dd_e);
}
void EnumMember::emitComment(Scope *sc)
{
//printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment);
if (prot() == PROTprivate)
return;
if (!comment)
return;
OutBuffer *buf = sc->docbuf;
DocComment *dc = DocComment::parse(sc, this, comment);
unsigned o;
if (!dc)
{
emitDitto(sc);
return;
}
dc->pmacrotable = &sc->module->macrotable;
buf->writestring(ddoc_decl_s);
o = buf->offset;
toDocBuffer(buf);
highlightCode(sc, this, buf, o);
sc->lastoffset = buf->offset;
buf->writestring(ddoc_decl_e);
buf->writestring(ddoc_decl_dd_s);
dc->writeSections(sc, this, buf);
buf->writestring(ddoc_decl_dd_e);
}
/******************************* toDocBuffer **********************************/
void Dsymbol::toDocBuffer(OutBuffer *buf)
{
//printf("Dsymbol::toDocbuffer() %s\n", toChars());
HdrGenState hgs;
hgs.ddoc = 1;
toCBuffer(buf, &hgs);
}
void prefix(OutBuffer *buf, Dsymbol *s)
{
if (s->isDeprecated())
buf->writestring("deprecated ");
Declaration *d = s->isDeclaration();
if (d)
{
emitProtection(buf, d->protection);
if (d->isAbstract())
buf->writestring("abstract ");
if (d->isStatic())
buf->writestring("static ");
if (d->isConst())
buf->writestring("const ");
#if DMDV2
if (d->isImmutable())
buf->writestring("immutable ");
#endif
if (d->isFinal())
buf->writestring("final ");
if (d->isSynchronized())
buf->writestring("synchronized ");
}
}
void declarationToDocBuffer(Declaration *decl, OutBuffer *buf, TemplateDeclaration *td)
{
//printf("declarationToDocBuffer() %s, originalType = %s, td = %s\n", decl->toChars(), decl->originalType ? decl->originalType->toChars() : "--", td ? td->toChars() : "--");
if (decl->ident)
{
prefix(buf, decl);
if (decl->type)
{ HdrGenState hgs;
hgs.ddoc = 1;
Type *origType = decl->originalType ? decl->originalType : decl->type;
if (origType->ty == Tfunction)
{
TypeFunction *attrType = (TypeFunction*)(decl->ident == Id::ctor ? origType : decl->type);
((TypeFunction*)origType)->toCBufferWithAttributes(buf, decl->ident, &hgs, attrType, td);
}
else
origType->toCBuffer(buf, decl->ident, &hgs);
}
else
buf->writestring(decl->ident->toChars());
buf->writestring(";\n");
}
}
void Declaration::toDocBuffer(OutBuffer *buf)
{
declarationToDocBuffer(this, buf, NULL);
}
void AliasDeclaration::toDocBuffer(OutBuffer *buf)
{
//printf("AliasDeclaration::toDocbuffer() %s\n", toChars());
if (ident)
{
if (isDeprecated())
buf->writestring("deprecated ");
emitProtection(buf, protection);
buf->writestring("alias ");
buf->writestring(toChars());
buf->writestring(";\n");
}
}
void TypedefDeclaration::toDocBuffer(OutBuffer *buf)
{
if (ident)
{
if (isDeprecated())
buf->writestring("deprecated ");
emitProtection(buf, protection);
buf->writestring("typedef ");
buf->writestring(toChars());
buf->writestring(";\n");
}
}
void FuncDeclaration::toDocBuffer(OutBuffer *buf)
{
//printf("FuncDeclaration::toDocbuffer() %s\n", toChars());
if (ident)
{
TemplateDeclaration *td;
if (parent &&
(td = parent->isTemplateDeclaration()) != NULL &&
td->onemember == this)
{ /* It's a function template
*/
unsigned o = buf->offset;
declarationToDocBuffer(this, buf, td);
highlightCode(NULL, this, buf, o);
}
else
{
Declaration::toDocBuffer(buf);
}
}
}
#if DMDV1
void CtorDeclaration::toDocBuffer(OutBuffer *buf)
{
HdrGenState hgs;
buf->writestring("this");
Parameter::argsToCBuffer(buf, &hgs, arguments, varargs);
buf->writestring(";\n");
}
#endif
void AggregateDeclaration::toDocBuffer(OutBuffer *buf)
{
if (ident)
{
#if 0
emitProtection(buf, protection);
#endif
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
buf->writestring(";\n");
}
}
void StructDeclaration::toDocBuffer(OutBuffer *buf)
{
//printf("StructDeclaration::toDocbuffer() %s\n", toChars());
if (ident)
{
#if 0
emitProtection(buf, protection);
#endif
TemplateDeclaration *td;
if (parent &&
(td = parent->isTemplateDeclaration()) != NULL &&
td->onemember == this)
{ unsigned o = buf->offset;
td->toDocBuffer(buf);
highlightCode(NULL, this, buf, o);
}
else
{
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
}
buf->writestring(";\n");
}
}
void ClassDeclaration::toDocBuffer(OutBuffer *buf)
{
//printf("ClassDeclaration::toDocbuffer() %s\n", toChars());
if (ident)
{
#if 0
emitProtection(buf, protection);
#endif
TemplateDeclaration *td;
if (parent &&
(td = parent->isTemplateDeclaration()) != NULL &&
td->onemember == this)
{ unsigned o = buf->offset;
td->toDocBuffer(buf);
highlightCode(NULL, this, buf, o);
}
else
{
if (isAbstract())
buf->writestring("abstract ");
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
}
int any = 0;
for (size_t i = 0; i < baseclasses->dim; i++)
{ BaseClass *bc = (*baseclasses)[i];
if (bc->protection == PROTprivate)
continue;
if (bc->base && bc->base->ident == Id::Object)
continue;
if (any)
buf->writestring(", ");
else
{ buf->writestring(": ");
any = 1;
}
emitProtection(buf, bc->protection);
if (bc->base)
{
buf->writestring(bc->base->toPrettyChars());
}
else
{
HdrGenState hgs;
bc->type->toCBuffer(buf, NULL, &hgs);
}
}
buf->writestring(";\n");
}
}
void EnumDeclaration::toDocBuffer(OutBuffer *buf)
{
if (ident)
{
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
buf->writestring(";\n");
}
}
void EnumMember::toDocBuffer(OutBuffer *buf)
{
if (ident)
{
buf->writestring(toChars());
}
}
/********************************* DocComment *********************************/
DocComment *DocComment::parse(Scope *sc, Dsymbol *s, unsigned char *comment)
{
//printf("parse(%s): '%s'\n", s->toChars(), comment);
if (sc->lastdc && isDitto(comment))
return NULL;
DocComment *dc = new DocComment();
if (!comment)
return dc;
dc->parseSections(comment);
for (size_t i = 0; i < dc->sections.dim; i++)
{ Section *sec = dc->sections[i];
if (icmp("copyright", sec->name, sec->namelen) == 0)
{
dc->copyright = sec;
}
if (icmp("macros", sec->name, sec->namelen) == 0)
{
dc->macros = sec;
}
}
sc->lastdc = dc;
return dc;
}
/*****************************************
* Parse next paragraph out of *pcomment.
* Update *pcomment to point past paragraph.
* Returns NULL if no more paragraphs.
* If paragraph ends in 'identifier:',
* then (*pcomment)[0 .. idlen] is the identifier.
*/
void DocComment::parseSections(unsigned char *comment)
{ unsigned char *p;
unsigned char *pstart;
unsigned char *pend;
unsigned char *idstart;
unsigned idlen;
unsigned char *name = NULL;
unsigned namelen = 0;
//printf("parseSections('%s')\n", comment);
p = comment;
while (*p)
{
p = skipwhitespace(p);
pstart = p;
pend = p;
/* Find end of section, which is ended by one of:
* 'identifier:' (but not inside a code section)
* '\0'
*/
idlen = 0;
int inCode = 0;
while (1)
{
// Check for start/end of a code section
if (*p == '-')
{
int numdash = 0;
while (*p == '-')
{
++numdash;
p++;
}
// BUG: handle UTF PS and LS too
if (!*p || *p == '\r' || *p == '\n' && numdash >= 3)
inCode ^= 1;
pend = p;
}
if (!inCode && isIdStart(p))
{
unsigned char *q = p + utfStride(p);
while (isIdTail(q))
q += utfStride(q);
if (*q == ':') // identifier: ends it
{ idlen = q - p;
idstart = p;
for (pend = p; pend > pstart; pend--)
{ if (pend[-1] == '\n')
break;
}
p = q + 1;
break;
}
}
while (1)
{
if (!*p)
goto L1;
if (*p == '\n')
{ p++;
if (*p == '\n' && !summary && !namelen && !inCode)
{
pend = p;
p++;
goto L1;
}
break;
}
p++;
pend = p;
}
p = skipwhitespace(p);
}
L1:
if (namelen || pstart < pend)
{
Section *s;
if (icmp("Params", name, namelen) == 0)
s = new ParamSection();
else if (icmp("Macros", name, namelen) == 0)
s = new MacroSection();
else
s = new Section();
s->name = name;
s->namelen = namelen;
s->body = pstart;
s->bodylen = pend - pstart;
s->nooutput = 0;
//printf("Section: '%.*s' = '%.*s'\n", s->namelen, s->name, s->bodylen, s->body);
sections.push(s);
if (!summary && !namelen)
summary = s;
}
if (idlen)
{ name = idstart;
namelen = idlen;
}
else
{ name = NULL;
namelen = 0;
if (!*p)
break;
}
}
}
void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf)
{
//printf("DocComment::writeSections()\n");
if (sections.dim)
{
buf->writestring("$(DDOC_SECTIONS \n");
for (size_t i = 0; i < sections.dim; i++)
{ Section *sec = sections[i];
if (sec->nooutput)
continue;
//printf("Section: '%.*s' = '%.*s'\n", sec->namelen, sec->name, sec->bodylen, sec->body);
if (sec->namelen || i)
sec->write(this, sc, s, buf);
else
{
buf->writestring("$(DDOC_SUMMARY ");
unsigned o = buf->offset;
buf->write(sec->body, sec->bodylen);
escapeStrayParenthesis(buf, o, s->loc);
highlightText(sc, s, buf, o);
buf->writestring(")\n");
}
}
buf->writestring(")\n");
}
else
{
buf->writestring("$(DDOC_BLANKLINE)\n");
}
}
/***************************************************
*/
void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf)
{
if (namelen)
{
static const char *table[] =
{ "AUTHORS", "BUGS", "COPYRIGHT", "DATE",
"DEPRECATED", "EXAMPLES", "HISTORY", "LICENSE",
"RETURNS", "SEE_ALSO", "STANDARDS", "THROWS",
"VERSION" };
for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++)
{
if (icmp(table[i], name, namelen) == 0)
{
buf->printf("$(DDOC_%s ", table[i]);
goto L1;
}
}
buf->writestring("$(DDOC_SECTION ");
// Replace _ characters with spaces
buf->writestring("$(DDOC_SECTION_H ");
unsigned o = buf->offset;
for (unsigned u = 0; u < namelen; u++)
{ unsigned char c = name[u];
buf->writeByte((c == '_') ? ' ' : c);
}
escapeStrayParenthesis(buf, o, s->loc);
buf->writestring(":)\n");
}
else
{
buf->writestring("$(DDOC_DESCRIPTION ");
}
L1:
unsigned o = buf->offset;
buf->write(body, bodylen);
escapeStrayParenthesis(buf, o, s->loc);
highlightText(sc, s, buf, o);
buf->writestring(")\n");
}
/***************************************************
*/
void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf)
{
unsigned char *p = body;
unsigned len = bodylen;
unsigned char *pend = p + len;
unsigned char *tempstart;
unsigned templen;
unsigned char *namestart;
unsigned namelen = 0; // !=0 if line continuation
unsigned char *textstart;
unsigned textlen;
unsigned o;
Parameter *arg;
buf->writestring("$(DDOC_PARAMS \n");
while (p < pend)
{
// Skip to start of macro
while (1)
{
switch (*p)
{
case ' ':
case '\t':
p++;
continue;
case '\n':
p++;
goto Lcont;
default:
if (isIdStart(p))
break;
if (namelen)
goto Ltext; // continuation of prev macro
goto Lskipline;
}
break;
}
tempstart = p;
while (isIdTail(p))
p += utfStride(p);
templen = p - tempstart;
while (*p == ' ' || *p == '\t')
p++;
if (*p != '=')
{ if (namelen)
goto Ltext; // continuation of prev macro
goto Lskipline;
}
p++;
if (namelen)
{ // Output existing param
L1:
//printf("param '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
HdrGenState hgs;
buf->writestring("$(DDOC_PARAM_ROW ");
buf->writestring("$(DDOC_PARAM_ID ");
o = buf->offset;
arg = isFunctionParameter(s, namestart, namelen);
if (arg && arg->type && arg->ident)
arg->type->toCBuffer(buf, arg->ident, &hgs);
else
buf->write(namestart, namelen);
escapeStrayParenthesis(buf, o, s->loc);
highlightCode(sc, s, buf, o);
buf->writestring(")\n");
buf->writestring("$(DDOC_PARAM_DESC ");
o = buf->offset;
buf->write(textstart, textlen);
escapeStrayParenthesis(buf, o, s->loc);
highlightText(sc, s, buf, o);
buf->writestring(")");
buf->writestring(")\n");
namelen = 0;
if (p >= pend)
break;
}
namestart = tempstart;
namelen = templen;
while (*p == ' ' || *p == '\t')
p++;
textstart = p;
Ltext:
while (*p != '\n')
p++;
textlen = p - textstart;
p++;
Lcont:
continue;
Lskipline:
// Ignore this line
while (*p++ != '\n')
;
}
if (namelen)
goto L1; // write out last one
buf->writestring(")\n");
}
/***************************************************
*/
void MacroSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf)
{
//printf("MacroSection::write()\n");
DocComment::parseMacros(dc->pescapetable, dc->pmacrotable, body, bodylen);
}
/************************************************
* Parse macros out of Macros: section.
* Macros are of the form:
* name1 = value1
*
* name2 = value2
*/
void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen)
{
unsigned char *p = m;
unsigned len = mlen;
unsigned char *pend = p + len;
unsigned char *tempstart;
unsigned templen;
unsigned char *namestart;
unsigned namelen = 0; // !=0 if line continuation
unsigned char *textstart;
unsigned textlen;
while (p < pend)
{
// Skip to start of macro
while (1)
{
if (p >= pend)
goto Ldone;
switch (*p)
{
case ' ':
case '\t':
p++;
continue;
case '\n':
p++;
goto Lcont;
default:
if (isIdStart(p))
break;
if (namelen)
goto Ltext; // continuation of prev macro
goto Lskipline;
}
break;
}
tempstart = p;
while (1)
{
if (p >= pend)
goto Ldone;
if (!isIdTail(p))
break;
p += utfStride(p);
}
templen = p - tempstart;
while (1)
{
if (p >= pend)
goto Ldone;
if (!(*p == ' ' || *p == '\t'))
break;
p++;
}
if (*p != '=')
{ if (namelen)
goto Ltext; // continuation of prev macro
goto Lskipline;
}
p++;
if (p >= pend)
goto Ldone;
if (namelen)
{ // Output existing macro
L1:
//printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
if (icmp("ESCAPES", namestart, namelen) == 0)
parseEscapes(pescapetable, textstart, textlen);
else
Macro::define(pmacrotable, namestart, namelen, textstart, textlen);
namelen = 0;
if (p >= pend)
break;
}
namestart = tempstart;
namelen = templen;
while (p < pend && (*p == ' ' || *p == '\t'))
p++;
textstart = p;
Ltext:
while (p < pend && *p != '\n')
p++;
textlen = p - textstart;
// Remove trailing \r if there is one
if (p > m && p[-1] == '\r')
textlen--;
p++;
//printf("p = %p, pend = %p\n", p, pend);
Lcont:
continue;
Lskipline:
// Ignore this line
while (p < pend && *p++ != '\n')
;
}
Ldone:
if (namelen)
goto L1; // write out last one
}
/**************************************
* Parse escapes of the form:
* /c/string/
* where c is a single character.
* Multiple escapes can be separated
* by whitespace and/or commas.
*/
void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen)
{ Escape *escapetable = *pescapetable;
if (!escapetable)
{ escapetable = new Escape;
*pescapetable = escapetable;
}
unsigned char *p = textstart;
unsigned char *pend = p + textlen;
while (1)
{
while (1)
{
if (p + 4 >= pend)
return;
if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ','))
break;
p++;
}
if (p[0] != '/' || p[2] != '/')
return;
unsigned char c = p[1];
p += 3;
unsigned char *start = p;
while (1)
{
if (p >= pend)
return;
if (*p == '/')
break;
p++;
}
size_t len = p - start;
char *s = (char *)memcpy(mem.malloc(len + 1), start, len);
s[len] = 0;
escapetable->strings[c] = s;
//printf("%c = '%s'\n", c, s);
p++;
}
}
/******************************************
* Compare 0-terminated string with length terminated string.
* Return < 0, ==0, > 0
*/
int cmp(const char *stringz, void *s, size_t slen)
{
size_t len1 = strlen(stringz);
if (len1 != slen)
return len1 - slen;
return memcmp(stringz, s, slen);
}
int icmp(const char *stringz, void *s, size_t slen)
{
size_t len1 = strlen(stringz);
if (len1 != slen)
return len1 - slen;
return memicmp(stringz, (char *)s, slen);
}
/*****************************************
* Return !=0 if comment consists entirely of "ditto".
*/
int isDitto(unsigned char *comment)
{
if (comment)
{
unsigned char *p = skipwhitespace(comment);
if (memicmp((char *)p, "ditto", 5) == 0 && *skipwhitespace(p + 5) == 0)
return 1;
}
return 0;
}
/**********************************************
* Skip white space.
*/
unsigned char *skipwhitespace(unsigned char *p)
{
for (; 1; p++)
{ switch (*p)
{
case ' ':
case '\t':
case '\n':
continue;
}
break;
}
return p;
}
/************************************************
* Scan forward to one of:
* start of identifier
* beginning of next line
* end of buf
*/
unsigned skiptoident(OutBuffer *buf, size_t i)
{
while (i < buf->offset)
{ dchar_t c;
size_t oi = i;
if (utf_decodeChar((unsigned char *)buf->data, buf->offset, &i, &c))
/* Ignore UTF errors, but still consume input
*/
break;
if (c >= 0x80)
{
if (!isUniAlpha(c))
continue;
}
else if (!(isalpha(c) || c == '_' || c == '\n'))
continue;
i = oi;
break;
}
return i;
}
/************************************************
* Scan forward past end of identifier.
*/
unsigned skippastident(OutBuffer *buf, size_t i)
{
while (i < buf->offset)
{ dchar_t c;
size_t oi = i;
if (utf_decodeChar((unsigned char *)buf->data, buf->offset, &i, &c))
/* Ignore UTF errors, but still consume input
*/
break;
if (c >= 0x80)
{
if (isUniAlpha(c))
continue;
}
else if (isalnum(c) || c == '_')
continue;
i = oi;
break;
}
return i;
}
/************************************************
* Scan forward past URL starting at i.
* We don't want to highlight parts of a URL.
* Returns:
* i if not a URL
* index just past it if it is a URL
*/
unsigned skippastURL(OutBuffer *buf, size_t i)
{ unsigned length = buf->offset - i;
unsigned char *p = &buf->data[i];
unsigned j;
unsigned sawdot = 0;
if (length > 7 && memicmp((char *)p, "http://", 7) == 0)
{
j = 7;
}
else if (length > 8 && memicmp((char *)p, "https://", 8) == 0)
{
j = 8;
}
else
goto Lno;
for (; j < length; j++)
{ unsigned char c = p[j];
if (isalnum(c))
continue;
if (c == '-' || c == '_' || c == '?' ||
c == '=' || c == '%' || c == '&' ||
c == '/' || c == '+' || c == '#' ||
c == '~')
continue;
if (c == '.')
{
sawdot = 1;
continue;
}
break;
}
if (sawdot)
return i + j;
Lno:
return i;
}
/****************************************************
*/
int isKeyword(unsigned char *p, unsigned len)
{
static const char *table[] = { "true", "false", "null" };
for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++)
{
if (cmp(table[i], p, len) == 0)
return 1;
}
return 0;
}
/****************************************************
*/
Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len)
{
FuncDeclaration *f = s->isFuncDeclaration();
/* f->type may be NULL for template members.
*/
if (f && f->type)
{
TypeFunction *tf;
if (f->originalType)
{
tf = (TypeFunction *)f->originalType;
}
else
tf = (TypeFunction *)f->type;
if (tf->parameters)
{
for (size_t k = 0; k < tf->parameters->dim; k++)
{ Parameter *arg = (*tf->parameters)[k];
if (arg->ident && cmp(arg->ident->toChars(), p, len) == 0)
{
return arg;
}
}
}
}
return NULL;
}
/**************************************************
* Highlight text section.
*/
void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset)
{
//printf("highlightText()\n");
const char *sid = s->ident->toChars();
FuncDeclaration *f = s->isFuncDeclaration();
unsigned char *p;
const char *se;
int leadingBlank = 1;
int inCode = 0;
//int inComment = 0; // in <!-- ... --> comment
unsigned iCodeStart; // start of code section
unsigned iLineStart = offset;
for (unsigned i = offset; i < buf->offset; i++)
{ unsigned char c = buf->data[i];
Lcont:
switch (c)
{
case ' ':
case '\t':
break;
case '\n':
if (sc && !inCode && i == iLineStart && i + 1 < buf->offset) // if "\n\n"
{
static char blankline[] = "$(DDOC_BLANKLINE)\n";
i = buf->insert(i, blankline, sizeof(blankline) - 1);
}
leadingBlank = 1;
iLineStart = i + 1;
break;
case '<':
leadingBlank = 0;
if (inCode)
break;
p = &buf->data[i];
// Skip over comments
if (p[1] == '!' && p[2] == '-' && p[3] == '-')
{ unsigned j = i + 4;
p += 4;
while (1)
{
if (j == buf->offset)
goto L1;
if (p[0] == '-' && p[1] == '-' && p[2] == '>')
{
i = j + 2; // place on closing '>'
break;
}
j++;
p++;
}
break;
}
// Skip over HTML tag
if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2])))
{ unsigned j = i + 2;
p += 2;
while (1)
{
if (j == buf->offset)
goto L1;
if (p[0] == '>')
{
i = j; // place on closing '>'
break;
}
j++;
p++;
}
break;
}
L1:
// Replace '<' with '&lt;' character entity
se = Escape::escapeChar('<');
if (se)
{ size_t len = strlen(se);
buf->remove(i, 1);
i = buf->insert(i, se, len);
i--; // point to ';'
}
break;
case '>':
leadingBlank = 0;
if (inCode)
break;
// Replace '>' with '&gt;' character entity
se = Escape::escapeChar('>');
if (se)
{ size_t len = strlen(se);
buf->remove(i, 1);
i = buf->insert(i, se, len);
i--; // point to ';'
}
break;
case '&':
leadingBlank = 0;
if (inCode)
break;
p = &buf->data[i];
if (p[1] == '#' || isalpha(p[1]))
break; // already a character entity
// Replace '&' with '&amp;' character entity
se = Escape::escapeChar('&');
if (se)
{ size_t len = strlen(se);
buf->remove(i, 1);
i = buf->insert(i, se, len);
i--; // point to ';'
}
break;
case '-':
/* A line beginning with --- delimits a code section.
* inCode tells us if it is start or end of a code section.
*/
if (leadingBlank)
{ int istart = i;
int eollen = 0;
leadingBlank = 0;
while (1)
{
++i;
if (i >= buf->offset)
break;
c = buf->data[i];
if (c == '\n')
{ eollen = 1;
break;
}
if (c == '\r')
{
eollen = 1;
if (i + 1 >= buf->offset)
break;
if (buf->data[i + 1] == '\n')
{ eollen = 2;
break;
}
}
// BUG: handle UTF PS and LS too
if (c != '-')
goto Lcont;
}
if (i - istart < 3)
goto Lcont;
// We have the start/end of a code section
// Remove the entire --- line, including blanks and \n
buf->remove(iLineStart, i - iLineStart + eollen);
i = iLineStart;
if (inCode && (i <= iCodeStart))
{ // Empty code section, just remove it completely.
inCode = 0;
break;
}
if (inCode)
{
inCode = 0;
// The code section is from iCodeStart to i
OutBuffer codebuf;
codebuf.write(buf->data + iCodeStart, i - iCodeStart);
codebuf.writeByte(0);
highlightCode2(sc, s, &codebuf, 0);
buf->remove(iCodeStart, i - iCodeStart);
i = buf->insert(iCodeStart, codebuf.data, codebuf.offset);
i = buf->insert(i, ")\n", 2);
i--;
}
else
{ static char pre[] = "$(D_CODE \n";
inCode = 1;
i = buf->insert(i, pre, sizeof(pre) - 1);
iCodeStart = i;
i--; // place i on >
leadingBlank = true;
}
}
break;
default:
leadingBlank = 0;
if (sc && !inCode && isIdStart(&buf->data[i]))
{ unsigned j;
j = skippastident(buf, i);
if (j > i)
{
unsigned k = skippastURL(buf, i);
if (k > i)
{ i = k - 1;
break;
}
if (buf->data[i] == '_') // leading '_' means no highlight
{
buf->remove(i, 1);
i = j - 1;
}
else
{
if (cmp(sid, buf->data + i, j - i) == 0)
{
i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
break;
}
else if (isKeyword(buf->data + i, j - i))
{
i = buf->bracket(i, "$(DDOC_KEYWORD ", j, ")") - 1;
break;
}
else
{
if (f && isFunctionParameter(f, buf->data + i, j - i))
{
//printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
break;
}
}
i = j - 1;
}
}
}
break;
}
}
if (inCode)
s->error("unmatched --- in DDoc comment");
;
}
/**************************************************
* Highlight code for DDOC section.
*/
void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset)
{
char *sid = s->ident->toChars();
FuncDeclaration *f = s->isFuncDeclaration();
//printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind());
for (unsigned i = offset; i < buf->offset; i++)
{ unsigned char c = buf->data[i];
const char *se;
se = Escape::escapeChar(c);
if (se)
{
size_t len = strlen(se);
buf->remove(i, 1);
i = buf->insert(i, se, len);
i--; // point to ';'
}
else if (isIdStart(&buf->data[i]))
{ unsigned j;
j = skippastident(buf, i);
if (j > i)
{
if (cmp(sid, buf->data + i, j - i) == 0)
{
i = buf->bracket(i, "$(DDOC_PSYMBOL ", j, ")") - 1;
continue;
}
else if (f)
{
if (isFunctionParameter(f, buf->data + i, j - i))
{
//printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1;
continue;
}
}
i = j - 1;
}
}
}
}
/****************************************
*/
void highlightCode3(OutBuffer *buf, unsigned char *p, unsigned char *pend)
{
for (; p < pend; p++)
{ const char *s = Escape::escapeChar(*p);
if (s)
buf->writestring(s);
else
buf->writeByte(*p);
}
}
/**************************************************
* Highlight code for CODE section.
*/
void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset)
{
char *sid = s->ident->toChars();
FuncDeclaration *f = s->isFuncDeclaration();
unsigned errorsave = global.errors;
Lexer lex(NULL, buf->data, 0, buf->offset - 1, 0, 1);
Token tok;
OutBuffer res;
unsigned char *lastp = buf->data;
const char *highlight;
//printf("highlightCode2('%.*s')\n", buf->offset - 1, buf->data);
res.reserve(buf->offset);
while (1)
{
lex.scan(&tok);
highlightCode3(&res, lastp, tok.ptr);
highlight = NULL;
switch (tok.value)
{
case TOKidentifier:
if (!sc)
break;
if (cmp(sid, tok.ptr, lex.p - tok.ptr) == 0)
{
highlight = "$(D_PSYMBOL ";
break;
}
else if (f)
{
if (isFunctionParameter(f, tok.ptr, lex.p - tok.ptr))
{
//printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j);
highlight = "$(D_PARAM ";
break;
}
}
break;
case TOKcomment:
highlight = "$(D_COMMENT ";
break;
case TOKstring:
highlight = "$(D_STRING ";
break;
default:
if (tok.isKeyword())
highlight = "$(D_KEYWORD ";
break;
}
if (highlight)
res.writestring(highlight);
highlightCode3(&res, tok.ptr, lex.p);
if (highlight)
res.writeByte(')');
if (tok.value == TOKeof)
break;
lastp = lex.p;
}
buf->setsize(offset);
buf->write(&res);
global.errors = errorsave;
}
/***************************************
* Find character string to replace c with.
*/
const char *Escape::escapeChar(unsigned c)
{ const char *s;
switch (c)
{
case '<':
s = "&lt;";
break;
case '>':
s = "&gt;";
break;
case '&':
s = "&amp;";
break;
default:
s = NULL;
break;
}
return s;
}
/****************************************
* Determine if p points to the start of an identifier.
*/
int isIdStart(unsigned char *p)
{
unsigned c = *p;
if (isalpha(c) || c == '_')
return 1;
if (c >= 0x80)
{ size_t i = 0;
if (utf_decodeChar(p, 4, &i, &c))
return 0; // ignore errors
if (isUniAlpha(c))
return 1;
}
return 0;
}
/****************************************
* Determine if p points to the rest of an identifier.
*/
int isIdTail(unsigned char *p)
{
unsigned c = *p;
if (isalnum(c) || c == '_')
return 1;
if (c >= 0x80)
{ size_t i = 0;
if (utf_decodeChar(p, 4, &i, &c))
return 0; // ignore errors
if (isUniAlpha(c))
return 1;
}
return 0;
}
/*****************************************
* Return number of bytes in UTF character.
*/
int utfStride(unsigned char *p)
{
unsigned c = *p;
if (c < 0x80)
return 1;
size_t i = 0;
utf_decodeChar(p, 4, &i, &c); // ignore errors, but still consume input
return i;
}
Jump to Line
Something went wrong with that request. Please try again.