Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2230 lines (1948 sloc) 60.707 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"
#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();
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/1.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->tdata()[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()
{
memset(this, 0, sizeof(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.