mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
2341 lines
62 KiB
C
2341 lines
62 KiB
C
|
|
// 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__ || __sun
|
|
#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;
|
|
size_t namelen;
|
|
|
|
unsigned char *body;
|
|
size_t 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, size_t mlen);
|
|
static void parseEscapes(Escape **pescapetable, unsigned char *textstart, size_t 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);
|
|
size_t skiptoident(OutBuffer *buf, size_t i);
|
|
size_t skippastident(OutBuffer *buf, size_t i);
|
|
size_t skippastURL(OutBuffer *buf, size_t i);
|
|
void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset);
|
|
void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset, bool anchor = true);
|
|
void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset);
|
|
Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, size_t 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_ANCHOR = <a name=\"$1\"></a>\n\
|
|
DDOC_PSYMBOL = $(U $0)\n\
|
|
DDOC_KEYWORD = $(B $0)\n\
|
|
DDOC_PARAM = $(I $0)\n\
|
|
\n\
|
|
ESCAPES = /</</\n\
|
|
/>/>/\n\
|
|
/&/&/\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]);
|
|
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, ¯otable, mbuf.data, mbuf.offset);
|
|
|
|
Scope *sc = Scope::createGlobal(this); // create root scope
|
|
sc->docbuf = &buf;
|
|
|
|
DocComment *dc = DocComment::parse(sc, this, comment);
|
|
dc->pmacrotable = ¯otable;
|
|
dc->pescapetable = &escapetable;
|
|
|
|
// Generate predefined macros
|
|
|
|
// Set the title to be the name of the module
|
|
{ const char *p = toPrettyChars();
|
|
Macro::define(¯otable, (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(¯otable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p));
|
|
Macro::define(¯otable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4);
|
|
}
|
|
|
|
char *srcfilename = srcfile->toChars();
|
|
Macro::define(¯otable, (unsigned char *)"SRCFILENAME", 11, (unsigned char *)srcfilename, strlen(srcfilename));
|
|
|
|
char *docfilename = docfile->toChars();
|
|
Macro::define(¯otable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename));
|
|
|
|
if (dc->copyright)
|
|
{
|
|
dc->copyright->nooutput = 1;
|
|
Macro::define(¯otable, (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(¯otable, (unsigned char *)"BODY", 4, buf.data, buf.offset);
|
|
|
|
OutBuffer buf2;
|
|
buf2.writestring("$(DDOC)\n");
|
|
size_t 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 (size_t 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
|
|
*/
|
|
{ size_t i = 0;
|
|
unsigned char *p = buf2.data;
|
|
for (size_t 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, size_t start)
|
|
{
|
|
for (size_t 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, size_t start, Loc loc)
|
|
{
|
|
unsigned par_open = 0;
|
|
|
|
for (size_t u = start; u < buf->offset; u++)
|
|
{
|
|
unsigned char c = buf->data[u];
|
|
switch(c)
|
|
{
|
|
case '(':
|
|
par_open++;
|
|
break;
|
|
|
|
case ')':
|
|
if (par_open == 0)
|
|
{
|
|
//stray ')'
|
|
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 (size_t u = buf->offset; u > start;)
|
|
{ u--;
|
|
unsigned char c = buf->data[u];
|
|
switch(c)
|
|
{
|
|
case ')':
|
|
par_open++;
|
|
break;
|
|
|
|
case '(':
|
|
if (par_open == 0)
|
|
{
|
|
//stray '('
|
|
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;
|
|
size_t o;
|
|
OutBuffer b;
|
|
|
|
b.writestring("$(DDOC_DITTO ");
|
|
o = b.offset;
|
|
toDocBuffer(&b, sc);
|
|
//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 = (prot == PROTpublic) ? NULL : Pprotectionnames[prot];
|
|
|
|
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);
|
|
size_t o;
|
|
|
|
if (!dc)
|
|
{
|
|
emitDitto(sc);
|
|
return;
|
|
}
|
|
dc->pmacrotable = &sc->module->macrotable;
|
|
|
|
buf->writestring(ddoc_decl_s);
|
|
o = buf->offset;
|
|
toDocBuffer(buf, sc);
|
|
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);
|
|
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);
|
|
size_t o;
|
|
|
|
if (!dc)
|
|
{
|
|
ss->emitDitto(sc);
|
|
return;
|
|
}
|
|
dc->pmacrotable = &sc->module->macrotable;
|
|
|
|
buf->writestring(ddoc_decl_s);
|
|
o = buf->offset;
|
|
ss->toDocBuffer(buf, sc);
|
|
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);
|
|
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);
|
|
size_t o;
|
|
|
|
if (!dc)
|
|
{
|
|
emitDitto(sc);
|
|
return;
|
|
}
|
|
dc->pmacrotable = &sc->module->macrotable;
|
|
|
|
buf->writestring(ddoc_decl_s);
|
|
o = buf->offset;
|
|
toDocBuffer(buf, sc);
|
|
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);
|
|
}
|
|
|
|
static bool emitAnchorName(OutBuffer *buf, Dsymbol *s)
|
|
{
|
|
if (!s || s->isPackage() || s->isModule())
|
|
return false;
|
|
|
|
TemplateDeclaration *td;
|
|
bool dot;
|
|
|
|
// Add parent names first
|
|
dot = emitAnchorName(buf, s->parent);
|
|
// Eponymous template members can share the parent anchor name
|
|
if (s->parent && (td = s->parent->isTemplateDeclaration()) != NULL &&
|
|
td->onemember == s)
|
|
return dot;
|
|
if (dot)
|
|
buf->writeByte('.');
|
|
// Use "this" not "__ctor"
|
|
if (s->isCtorDeclaration() || ((td = s->isTemplateDeclaration()) != NULL &&
|
|
td->onemember && td->onemember->isCtorDeclaration()))
|
|
buf->writestring("this");
|
|
else
|
|
{
|
|
/* We just want the identifier, not overloads like TemplateDeclaration::toChars.
|
|
* We don't want the template parameter list and constraints. */
|
|
buf->writestring(s->Dsymbol::toChars());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void emitAnchor(OutBuffer *buf, Dsymbol *s)
|
|
{
|
|
buf->writestring("$(DDOC_ANCHOR ");
|
|
emitAnchorName(buf, s);
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
/******************************* toDocBuffer **********************************/
|
|
|
|
void Dsymbol::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
//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->isStatic())
|
|
buf->writestring("static ");
|
|
else if (d->isFinal())
|
|
buf->writestring("final ");
|
|
else if (d->isAbstract())
|
|
buf->writestring("abstract ");
|
|
|
|
if (d->isConst())
|
|
buf->writestring("const ");
|
|
#if DMDV2
|
|
if (d->isImmutable())
|
|
buf->writestring("immutable ");
|
|
#endif
|
|
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, Scope *sc)
|
|
{
|
|
declarationToDocBuffer(this, buf, NULL);
|
|
}
|
|
|
|
void AliasDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
//printf("AliasDeclaration::toDocbuffer() %s\n", toChars());
|
|
if (ident)
|
|
{
|
|
if (isDeprecated())
|
|
buf->writestring("deprecated ");
|
|
|
|
emitProtection(buf, protection);
|
|
buf->writestring("alias ");
|
|
|
|
if (Dsymbol *s = aliassym) // ident alias
|
|
{
|
|
prettyPrintDsymbol(buf, s, parent);
|
|
}
|
|
else if (Type *type = getType()) // type alias
|
|
{
|
|
if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum)
|
|
{
|
|
if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type
|
|
prettyPrintDsymbol(buf, s, parent);
|
|
else
|
|
buf->writestring(type->toChars());
|
|
}
|
|
else
|
|
{
|
|
// simple type
|
|
buf->writestring(type->toChars());
|
|
}
|
|
}
|
|
|
|
buf->writestring(" ");
|
|
buf->writestring(toChars());
|
|
buf->writestring(";\n");
|
|
}
|
|
}
|
|
|
|
void parentToBuffer(OutBuffer *buf, Dsymbol *s)
|
|
{
|
|
if (s && !s->isPackage() && !s->isModule())
|
|
{
|
|
parentToBuffer(buf, s->parent);
|
|
buf->writestring(s->toChars());
|
|
buf->writestring(".");
|
|
}
|
|
}
|
|
|
|
bool inSameModule(Dsymbol *s, Dsymbol *p)
|
|
{
|
|
for ( ; s ; s = s->parent)
|
|
{
|
|
if (s->isModule())
|
|
break;
|
|
}
|
|
|
|
for ( ; p ; p = p->parent)
|
|
{
|
|
if (p->isModule())
|
|
break;
|
|
}
|
|
|
|
return s == p;
|
|
}
|
|
|
|
void prettyPrintDsymbol(OutBuffer *buf, Dsymbol *s, Dsymbol *parent)
|
|
{
|
|
if (s->parent && (s->parent == parent)) // in current scope -> naked name
|
|
{
|
|
buf->writestring(s->toChars());
|
|
}
|
|
else
|
|
if (!inSameModule(s, parent)) // in another module -> full name
|
|
{
|
|
buf->writestring(s->toPrettyChars());
|
|
}
|
|
else // nested in a type in this module -> full name w/o module name
|
|
{
|
|
// if alias is nested in a user-type use module-scope lookup
|
|
if (!parent->isModule() && !parent->isPackage())
|
|
buf->writestring(".");
|
|
|
|
parentToBuffer(buf, s->parent);
|
|
buf->writestring(s->toChars());
|
|
}
|
|
}
|
|
|
|
void TypedefDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
if (ident)
|
|
{
|
|
if (isDeprecated())
|
|
buf->writestring("deprecated ");
|
|
|
|
emitProtection(buf, protection);
|
|
buf->writestring("typedef ");
|
|
buf->writestring(toChars());
|
|
buf->writestring(";\n");
|
|
}
|
|
}
|
|
|
|
|
|
void FuncDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
//printf("FuncDeclaration::toDocbuffer() %s\n", toChars());
|
|
if (ident)
|
|
{
|
|
TemplateDeclaration *td;
|
|
|
|
if (parent &&
|
|
(td = parent->isTemplateDeclaration()) != NULL &&
|
|
td->onemember == this)
|
|
{ /* It's a function template
|
|
*/
|
|
size_t o = buf->offset;
|
|
|
|
declarationToDocBuffer(this, buf, td);
|
|
|
|
highlightCode(NULL, this, buf, o);
|
|
}
|
|
else
|
|
{
|
|
Declaration::toDocBuffer(buf, sc);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DMDV1
|
|
void CtorDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
HdrGenState hgs;
|
|
|
|
buf->writestring("this");
|
|
Parameter::argsToCBuffer(buf, &hgs, arguments, varargs);
|
|
buf->writestring(";\n");
|
|
}
|
|
#endif
|
|
|
|
void AggregateDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
if (ident)
|
|
{
|
|
emitAnchor(buf, this);
|
|
#if 0
|
|
emitProtection(buf, protection);
|
|
#endif
|
|
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
|
|
buf->writestring(";\n");
|
|
}
|
|
}
|
|
|
|
void StructDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
//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)
|
|
{ size_t o = buf->offset;
|
|
td->toDocBuffer(buf, sc);
|
|
highlightCode(NULL, this, buf, o);
|
|
}
|
|
else
|
|
{
|
|
emitAnchor(buf, this);
|
|
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
|
|
}
|
|
buf->writestring(";\n");
|
|
}
|
|
}
|
|
|
|
void ClassDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
//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)
|
|
{ size_t o = buf->offset;
|
|
td->toDocBuffer(buf, sc);
|
|
highlightCode(NULL, this, buf, o);
|
|
}
|
|
else
|
|
{
|
|
emitAnchor(buf, this);
|
|
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, Scope *sc)
|
|
{
|
|
if (ident)
|
|
{
|
|
emitAnchor(buf, this);
|
|
buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars());
|
|
buf->writestring(";\n");
|
|
}
|
|
}
|
|
|
|
void EnumMember::toDocBuffer(OutBuffer *buf, Scope *sc)
|
|
{
|
|
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;
|
|
size_t idlen;
|
|
|
|
unsigned char *name = NULL;
|
|
size_t 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 ");
|
|
size_t 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 (size_t 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 ");
|
|
size_t o = buf->offset;
|
|
for (size_t 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:
|
|
size_t 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;
|
|
size_t len = bodylen;
|
|
unsigned char *pend = p + len;
|
|
|
|
unsigned char *tempstart;
|
|
size_t templen;
|
|
|
|
unsigned char *namestart;
|
|
size_t namelen = 0; // !=0 if line continuation
|
|
|
|
unsigned char *textstart;
|
|
size_t textlen;
|
|
|
|
size_t 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, false);
|
|
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, size_t mlen)
|
|
{
|
|
unsigned char *p = m;
|
|
size_t len = mlen;
|
|
unsigned char *pend = p + len;
|
|
|
|
unsigned char *tempstart;
|
|
size_t templen;
|
|
|
|
unsigned char *namestart;
|
|
size_t namelen = 0; // !=0 if line continuation
|
|
|
|
unsigned char *textstart;
|
|
size_t 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, size_t 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
|
|
*/
|
|
|
|
size_t 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.
|
|
*/
|
|
|
|
size_t 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
|
|
*/
|
|
|
|
size_t skippastURL(OutBuffer *buf, size_t i)
|
|
{ size_t length = buf->offset - i;
|
|
unsigned char *p = &buf->data[i];
|
|
size_t 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, size_t 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, size_t 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, size_t 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
|
|
size_t iCodeStart; // start of code section
|
|
|
|
size_t iLineStart = offset;
|
|
|
|
for (size_t 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] == '-')
|
|
{ size_t 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])))
|
|
{ size_t 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 '<' 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 '>' 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 '&' 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)
|
|
{ size_t istart = i;
|
|
size_t 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 -= 2; // in next loop, c should be '\n'
|
|
}
|
|
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]))
|
|
{
|
|
size_t j = skippastident(buf, i);
|
|
if (j > i)
|
|
{
|
|
size_t 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, size_t offset, bool anchor)
|
|
{
|
|
if (anchor)
|
|
{
|
|
OutBuffer ancbuf;
|
|
|
|
emitAnchor(&ancbuf, s);
|
|
buf->insert(offset, (char *)ancbuf.data, ancbuf.offset);
|
|
offset += ancbuf.offset;
|
|
}
|
|
char *sid = s->ident->toChars();
|
|
FuncDeclaration *f = s->isFuncDeclaration();
|
|
|
|
//printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind());
|
|
for (size_t 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]))
|
|
{
|
|
size_t 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, size_t 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 = "<";
|
|
break;
|
|
case '>':
|
|
s = ">";
|
|
break;
|
|
case '&':
|
|
s = "&";
|
|
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;
|
|
}
|