Add C# documentation generation

This commit is contained in:
frederic.bon 2024-03-15 14:20:33 +01:00
parent 1e2b0b8079
commit f7b8ed4d7e
4 changed files with 1467 additions and 15 deletions

View File

@ -0,0 +1,970 @@
/* -----------------------------------------------------------------------------
* This file is part of SWIG, which is licensed as a whole under version 3
* (or any later version) of the GNU General Public License. Some additional
* terms also apply to certain portions of SWIG. The full details of the SWIG
* license and copyrights can be found in the LICENSE and COPYRIGHT files
* included with the SWIG source code as distributed by the SWIG developers
* and at http://www.swig.org/legal.html.
*
* csharpdoc.cxx
*
* Module to return documentation for nodes formatted for CSharpDoc
* ----------------------------------------------------------------------------- */
#include "csharpdoc.h"
#include "doxyparser.h"
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include "swigmod.h"
// define static tables, they are filled in CSharpDocConverter's constructor
CSharpDocConverter::TagHandlersMap CSharpDocConverter::tagHandlers;
using std::string;
// Helper class increasing the provided indent string in its ctor and decreasing
// it in its dtor.
class IndentGuard {
public:
// One indent level.
static const char *Level() {
return " ";
}
// Default ctor doesn't do anything and prevents the dtor from doing anything// too and should only be used when the guard needs to be initialized// conditionally as Init() can then be called after checking some condition.// Otherwise, prefer to use the non default ctor below.
IndentGuard() {
m_initialized = false;
}
// Ctor takes the output to determine the current indent and to remove the
// extra indent added to it in the dtor and the variable containing the indent
// to use, which must be used after every new line by the code actually
// updating the output.
IndentGuard(string &output, string &indent) {
Init(output, indent);
}
// Really initializes the object created using the default ctor.
void Init(string &output, string &indent) {
m_output = &output;
m_indent = &indent;
const string::size_type lastNonSpace = m_output->find_last_not_of(' ');
if (lastNonSpace == string::npos) {
m_firstLineIndent = m_output->length();
} else if ((*m_output)[lastNonSpace] == '\n') {
m_firstLineIndent = m_output->length() - (lastNonSpace + 1);
} else {
m_firstLineIndent = 0;
}
// Notice that the indent doesn't include the first line indent because it's
// implicit, i.e. it is present in the input and so is copied into the
// output anyhow.
*m_indent = Level();
m_initialized = true;
}
// Get the indent for the first line of the paragraph, which is smaller than
// the indent for the subsequent lines.
string getFirstLineIndent() const {
return string(m_firstLineIndent, ' ');
}
~IndentGuard() {
if (!m_initialized)
return;
m_indent->clear();
// Get rid of possible remaining extra indent, e.g. if there were any trailing
// new lines: we shouldn't add the extra indent level to whatever follows
// this paragraph.
static const size_t lenIndentLevel = strlen(Level());
if (m_output->length() > lenIndentLevel) {
const size_t start = m_output->length() - lenIndentLevel;
if (m_output->compare(start, string::npos, Level()) == 0)
m_output->erase(start);
}
}
private:
string *m_output;
string *m_indent;
string::size_type m_firstLineIndent;
bool m_initialized;
IndentGuard(const IndentGuard &);
};
static void trimWhitespace(string &s) {
const string::size_type lastNonSpace = s.find_last_not_of(' ');
if (lastNonSpace == string::npos)
s.clear();
else
s.erase(lastNonSpace + 1);
}
// Erase the first character in the string if it is a newline
static void eraseLeadingNewLine(string &s) {
if (!s.empty() && s[0] == '\n')
s.erase(s.begin());
}
// Erase the first character in the string if it is a newline
static void eraseAllNewLine(string &str) {
for (size_t i = 0; i < str.size(); i++) {
// if the character is a newline character
if (str[i] == '\n') {
// remove the character
str.erase(i, 1);
// decrement the index to account for the removed character
i--;
}
}
}
// Erase last characters in the string if it is a newline or a space
static void eraseTrailingSpaceNewLines(string &s) {
while (!s.empty() && (s[s.size() - 1] == '\n' || s[s.size() - 1] == ' '))
s.erase(s.size() - 1);
}
// escape some characters which cannot appear as it in C# comments
static void escapeSpecificCharacters(string & str)
{
for (size_t i = 0; i < str.size(); i++) {
if (str[i] == '<') {
str.replace(i, 1, "&lt;");
}
else if(str[i] == '>') {
str.replace(i, 1, "&gt;");
}
else if (str[i] == '&') {
str.replace(i, 1, "&amp;");
}
}
}
// Check the generated docstring line by line and make sure that any
// code and verbatim blocks have an empty line preceding them, which
// is necessary for Sphinx. Additionally, this strips any empty lines
// appearing at the beginning of the docstring.
static string padCodeAndVerbatimBlocks(const string &docString) {
std::string result;
std::istringstream iss(docString);
// Initialize to false because there is no previous line yet
bool lastLineWasNonBlank = false;
for (string line; std::getline(iss, line); result += line) {
if (!result.empty()) {
// Terminate the previous line
result += '\n';
}
const size_t pos = line.find_first_not_of(" \t");
if (pos == string::npos) {
lastLineWasNonBlank = false;
} else {
if (lastLineWasNonBlank &&
(line.compare(pos, 13, ".. code-block") == 0 ||
line.compare(pos, 7, ".. math") == 0 ||
line.compare(pos, 3, ">>>") == 0)) {
// Must separate code or math blocks from the previous line
result += '\n';
}
lastLineWasNonBlank = true;
}
}
return result;
}
// Helper function to extract the option value from a command,
// e.g. param[in] -> in
static std::string getCommandOption(const std::string &command, char openChar, char closeChar) {
string option;
size_t opt_begin, opt_end;
opt_begin = command.find(openChar);
opt_end = command.find(closeChar);
if (opt_begin != string::npos && opt_end != string::npos)
option = command.substr(opt_begin+1, opt_end-opt_begin-1);
return option;
}
/* static */
CSharpDocConverter::TagHandlersMap::mapped_type CSharpDocConverter::make_handler(tagHandler handler) {
return make_pair(handler, std::string());
}
/* static */
CSharpDocConverter::TagHandlersMap::mapped_type CSharpDocConverter::make_handler(tagHandler handler, const char *arg) {
return make_pair(handler, arg);
}
void CSharpDocConverter::fillStaticTables() {
if (tagHandlers.size()) // fill only once
return;
tagHandlers["a"] = make_handler(&CSharpDocConverter::handleTagWrap, "*");
tagHandlers["b"] = make_handler(&CSharpDocConverter::handleTagWrap, "**");
// \c command is translated as single quotes around next word
tagHandlers["c"] = make_handler(&CSharpDocConverter::handleTagWrap, "``");
tagHandlers["cite"] = make_handler(&CSharpDocConverter::handleTagWrap, "'");
tagHandlers["e"] = make_handler(&CSharpDocConverter::handleTagWrap, "*");
// these commands insert just a single char, some of them need to be escaped
tagHandlers["$"] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["@"] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["\\"] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["<"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&lt;");
tagHandlers[">"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&gt;");
tagHandlers["&"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&amp;");
tagHandlers["#"] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["%"] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["~"] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["\""] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["."] = make_handler(&CSharpDocConverter::handleTagChar);
tagHandlers["::"] = make_handler(&CSharpDocConverter::handleTagChar);
// these commands are stripped out, and only their content is printed
tagHandlers["attention"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
tagHandlers["author"] = make_handler(&CSharpDocConverter::handleTagWord, "Author");
tagHandlers["authors"] = make_handler(&CSharpDocConverter::handleTagWord, "Author");
tagHandlers["brief"] = make_handler(&CSharpDocConverter::handleSummary);
tagHandlers["bug"] = make_handler(&CSharpDocConverter::handleTagWord, "Bug:");
tagHandlers["code"] = make_handler(&CSharpDocConverter::handleCode);
tagHandlers["copyright"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
tagHandlers["date"] = make_handler(&CSharpDocConverter::handleTagWord, "Date");
tagHandlers["deprecated"] = make_handler(&CSharpDocConverter::handleTagWord, "Deprecated");
tagHandlers["details"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
tagHandlers["em"] = make_handler(&CSharpDocConverter::handleTagWrap, "*");
tagHandlers["example"] = make_handler(&CSharpDocConverter::handleTagWord, "Example");
tagHandlers["exception"] = tagHandlers["throw"] = tagHandlers["throws"] = make_handler(&CSharpDocConverter::handleTagException);
tagHandlers["htmlonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["invariant"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["latexonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["link"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["manonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["note"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["p"] = make_handler(&CSharpDocConverter::handleTagWrap, "``");
tagHandlers["partofdescription"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["rtfonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["remark"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
tagHandlers["remarks"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
tagHandlers["sa"] = make_handler(&CSharpDocConverter::handleTagSee);
tagHandlers["see"] = make_handler(&CSharpDocConverter::handleTagSee);
tagHandlers["since"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["short"] = make_handler(&CSharpDocConverter::handleNotHandled);
tagHandlers["todo"] = make_handler(&CSharpDocConverter::handleTagWord, "TODO");
tagHandlers["version"] = make_handler(&CSharpDocConverter::handleTagWord, "Version");
tagHandlers["verbatim"] = make_handler(&CSharpDocConverter::handleVerbatimBlock);
tagHandlers["warning"] = make_handler(&CSharpDocConverter::handleLine, "remarks");
tagHandlers["xmlonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
// these commands have special handlers
#if 0
tagHandlers["arg"] = make_handler(&CSharpDocConverter::handleTagMessage, "* ");
tagHandlers["cond"] = make_handler(&CSharpDocConverter::handleTagMessage, "Conditional comment: ");
tagHandlers["else"] = make_handler(&CSharpDocConverter::handleTagIf, "Else: ");
tagHandlers["elseif"] = make_handler(&CSharpDocConverter::handleTagIf, "Else if: ");
tagHandlers["endcond"] = make_handler(&CSharpDocConverter::handleTagMessage, "End of conditional comment.");
tagHandlers["if"] = make_handler(&CSharpDocConverter::handleTagIf, "If: ");
tagHandlers["ifnot"] = make_handler(&CSharpDocConverter::handleTagIf, "If not: ");
tagHandlers["image"] = make_handler(&CSharpDocConverter::handleTagImage);
tagHandlers["li"] = make_handler(&CSharpDocConverter::handleTagMessage, "* ");
tagHandlers["overload"] = make_handler(&CSharpDocConverter::handleTagMessage,
"This is an overloaded member function, provided for"
" convenience.\nIt differs from the above function only in what" " argument(s) it accepts.");
#else
tagHandlers["arg"] = make_handler(&CSharpDocConverter::handleAddList);
tagHandlers["cond"] = make_handler(&CSharpDocConverter::handleIgnore);
tagHandlers["else"] = make_handler(&CSharpDocConverter::handleTagWord, "Else");
tagHandlers["elseif"] = make_handler(&CSharpDocConverter::handleTagWord, "Else If");
tagHandlers["endcond"] = make_handler(&CSharpDocConverter::handleIgnore);
tagHandlers["if"] = make_handler(&CSharpDocConverter::handleTagWord, "If");
tagHandlers["ifnot"] = make_handler(&CSharpDocConverter::handleTagWord, "If Not");
tagHandlers["image"] = make_handler(&CSharpDocConverter::handleIgnore);
tagHandlers["li"] = make_handler(&CSharpDocConverter::handleIgnore);
tagHandlers["overload"] = make_handler(&CSharpDocConverter::handleIgnore);
#endif
tagHandlers["par"] = make_handler(&CSharpDocConverter::handleTagWord, "Title");
tagHandlers["param"] = tagHandlers["tparam"] = make_handler(&CSharpDocConverter::handleTagParam);
tagHandlers["ref"] = make_handler(&CSharpDocConverter::handleTagRef);
tagHandlers["result"] = tagHandlers["return"] = tagHandlers["returns"] = make_handler(&CSharpDocConverter::handleTagReturn);
// this command just prints its contents
// (it is internal command of swig's parser, contains plain text)
tagHandlers["plainstd::string"] = make_handler(&CSharpDocConverter::handlePlainString);
tagHandlers["plainstd::endl"] = make_handler(&CSharpDocConverter::handleNewLine);
tagHandlers["n"] = make_handler(&CSharpDocConverter::handleNewLine);
// \f commands output literal Latex formula, which is still better than nothing.
tagHandlers["f$"] = tagHandlers["f["] = tagHandlers["f{"] = make_handler(&CSharpDocConverter::handleMath);
// HTML tags
tagHandlers["<a"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_A);
tagHandlers["<b"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "**");
tagHandlers["<blockquote"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_A, "Quote: ");
tagHandlers["<body"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<br"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "\n");
// there is no formatting for this tag as it was deprecated in HTML 4.01 and
// not used in HTML 5
tagHandlers["<center"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<caption"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<code"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "``");
tagHandlers["<dl"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<dd"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, " ");
tagHandlers["<dt"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<dfn"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<div"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<em"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "**");
tagHandlers["<form"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<hr"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "--------------------------------------------------------------------\n");
tagHandlers["<h1"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "# ");
tagHandlers["<h2"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "## ");
tagHandlers["<h3"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "### ");
tagHandlers["<i"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "*");
tagHandlers["<input"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<img"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "Image:");
tagHandlers["<li"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "* ");
tagHandlers["<meta"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<multicol"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<ol"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<p"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "\n");
tagHandlers["<pre"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<small"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<span"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "'");
tagHandlers["<strong"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "**");
// make a space between text and super/sub script.
tagHandlers["<sub"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, " ");
tagHandlers["<sup"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, " ");
tagHandlers["<table"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTagNoParam);
tagHandlers["<td"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_td);
tagHandlers["<th"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_th);
tagHandlers["<tr"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_tr);
tagHandlers["<tt"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<kbd"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<ul"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
tagHandlers["<var"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "*");
// HTML entities
tagHandlers["&copy"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "(C)");
tagHandlers["&trade"] = make_handler(&CSharpDocConverter::handleHtmlEntity, " TM");
tagHandlers["&reg"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "(R)");
tagHandlers["&lt"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<");
tagHandlers["&gt"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ">");
tagHandlers["&amp"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "&");
tagHandlers["&apos"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "'");
tagHandlers["&quot"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "\"");
tagHandlers["&lsquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "`");
tagHandlers["&rsquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "'");
tagHandlers["&ldquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "\"");
tagHandlers["&rdquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "\"");
tagHandlers["&ndash"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "-");
tagHandlers["&mdash"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "--");
tagHandlers["&nbsp"] = make_handler(&CSharpDocConverter::handleHtmlEntity, " ");
tagHandlers["&times"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "x");
tagHandlers["&minus"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "-");
tagHandlers["&sdot"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ".");
tagHandlers["&sim"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "~");
tagHandlers["&le"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<=");
tagHandlers["&ge"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ">=");
tagHandlers["&larr"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<--");
tagHandlers["&rarr"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "-->");
}
CSharpDocConverter::CSharpDocConverter(int flags):
DoxygenTranslator(flags), m_tableLineLen(0), m_prevRowIsTH(false) {
fillStaticTables();
}
// Return the type as it should appear in the output documentation.
static std::string getCSharpDocType(Node *n, const_String_or_char_ptr lname = "") {
std::string type;
String *s = Swig_typemap_lookup("doctype", n, lname, 0);
if (!s) {
if (String *t = Getattr(n, "type"))
s = SwigType_str(t, "");
}
/////////////////
if (!s)
return type;
type = Char(s);
Delete(s);
return type;
}
std::string CSharpDocConverter::getParamType(std::string param) {
std::string type;
ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
for (Parm *p = plist; p; p = nextSibling(p)) {
String *pname = Getattr(p, "name");
if (pname && Char(pname) == param) {
type = getCSharpDocType(p, pname);
break;
}
}
Delete(plist);
return type;
}
std::string CSharpDocConverter::getParamValue(std::string param) {
std::string value;
ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
for (Parm *p = plist; p; p = nextSibling(p)) {
String *pname = Getattr(p, "name");
if (pname && Char(pname) == param) {
String *pval = Getattr(p, "value");
if (pval)
value = Char(pval);
break;
}
}
Delete(plist);
return value;
}
std::string CSharpDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
std::string translatedComment;
if (doxygenEntity.isLeaf)
return translatedComment;
std::string currentSection;
std::list<DoxygenEntity>::iterator p = doxygenEntity.entityList.begin();
while (p != doxygenEntity.entityList.end()) {
translateEntity(*p, translatedComment);
translateSubtree(*p);
p++;
}
return translatedComment;
}
void CSharpDocConverter::translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment) {
// check if we have needed handler and call it
std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
it = tagHandlers.find(getBaseCommand(doxyEntity.typeOfEntity));
if (it != tagHandlers.end())
(this->*(it->second.first)) (doxyEntity, translatedComment, it->second.second);
}
void CSharpDocConverter::handleIgnore(DoxygenEntity & tag, std::string &translatedComment, const std::string &)
{
if (tag.entityList.size()) {
tag.entityList.pop_front();
}
translatedComment += translateSubtree(tag);
}
void CSharpDocConverter::handleSummary(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
translatedComment += "<summary>";
std::string summary = translateSubtree(tag);
eraseAllNewLine(summary);
trimWhitespace(summary);
// remove final newlines
eraseTrailingSpaceNewLines(summary);
escapeSpecificCharacters(summary);
translatedComment += summary;
translatedComment += "</summary>";
translatedComment += "\n";
}
void CSharpDocConverter::handleLine(DoxygenEntity &tag, std::string &translatedComment, const std::string & tagName) {
translatedComment += "<" + tagName +">";
if (tag.entityList.size()) {
translatedComment += tag.entityList.begin()->data;
tag.entityList.pop_front();
}
translatedComment += "</" + tagName +">";
}
void CSharpDocConverter::handleNotHandled(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
std::string paragraph = translateSubtree(tag);
eraseLeadingNewLine(paragraph);
eraseTrailingSpaceNewLines(paragraph);
trimWhitespace(paragraph);
escapeSpecificCharacters(paragraph);
translatedComment += paragraph;
translatedComment += "\n";
}
void CSharpDocConverter::handleAddList(DoxygenEntity &tag, std::string &translatedComment, const std::string &)
{
std::string listItem = translateSubtree(tag);
eraseAllNewLine(listItem);
translatedComment += "* ";
translatedComment += listItem;
translatedComment += "\n";
}
void CSharpDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string & tagName) {
translatedComment += "<";
translatedComment += tagName;
translatedComment += ">";
std::string paragraph = translateSubtree(tag);
eraseAllNewLine(paragraph);
trimWhitespace(paragraph);
eraseTrailingSpaceNewLines(paragraph);
escapeSpecificCharacters(paragraph);
translatedComment += paragraph;
translatedComment += "</";
translatedComment += tagName;
translatedComment += ">\n";
}
void CSharpDocConverter::handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
string verb = translateSubtree(tag);
eraseLeadingNewLine(verb);
// Remove the last newline to prevent doubling the newline already present after \endverbatim
trimWhitespace(verb); // Needed to catch trailing newline below
eraseTrailingSpaceNewLines(verb);
escapeSpecificCharacters(verb);
translatedComment += verb;
}
void CSharpDocConverter::handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
IndentGuard indent;
// Only \f$ is translated to inline formulae, \f[ and \f{ are for the block ones.
const bool inlineFormula = tag.typeOfEntity == "f$";
string formulaNL;
if (inlineFormula) {
translatedComment += ":math:`";
} else {
indent.Init(translatedComment, m_indent);
trimWhitespace(translatedComment);
const string formulaIndent = indent.getFirstLineIndent();
translatedComment += formulaIndent;
translatedComment += ".. math::\n";
formulaNL = '\n';
formulaNL += formulaIndent;
formulaNL += m_indent;
translatedComment += formulaNL;
}
std::string formula;
handleTagVerbatim(tag, formula, arg);
// It is important to ensure that we have no spaces around the inline math
// contents, so strip them.
const size_t start = formula.find_first_not_of(" \t\n");
const size_t end = formula.find_last_not_of(" \t\n");
if (start != std::string::npos) {
for (size_t n = start; n <= end; n++) {
if (formula[n] == '\n') {
// New lines must be suppressed in inline maths and indented in the block ones.
if (!inlineFormula)
translatedComment += formulaNL;
} else {
// Just copy everything else.
translatedComment += formula[n];
}
}
}
if (inlineFormula) {
translatedComment += "`";
}
}
void CSharpDocConverter::handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
IndentGuard indent(translatedComment, m_indent);
trimWhitespace(translatedComment);
translatedComment += "<code>";
std::string code;
handleTagVerbatim(tag, code, arg);
// Try and remove leading newline, which is present for block \code
// command:
escapeSpecificCharacters(code);
eraseLeadingNewLine(code);
trimWhitespace(code);
// Check for python doctest blocks, and treat them specially:
bool isDocTestBlock = false;
size_t startPos;
// ">>>" would normally appear at the beginning, but doxygen comment
// style may have space in front, so skip leading whitespace
if ((startPos=code.find_first_not_of(" \t")) != string::npos && code.substr(startPos,3) == ">>>")
isDocTestBlock = true;
string codeIndent;
if (! isDocTestBlock) {
// Use the current indent for the code-block line itself.
translatedComment += indent.getFirstLineIndent();
// Specify the level of extra indentation that will be used for
// subsequent lines within the code block. Note that the correct
// "starting indentation" is already present in the input, so we
// only need to add the desired code block indentation.
codeIndent = m_indent;
}
translatedComment += codeIndent;
for (size_t n = 0; n < code.length(); n++) {
if (code[n] == '\n') {
// Don't leave trailing white space, this results in PEP8 validation
// errors in Python code (which are performed by our own unit tests).
trimWhitespace(translatedComment);
translatedComment += '\n';
// Ensure that we indent all the lines by the code indent.
translatedComment += codeIndent;
} else {
// Just copy everything else.
translatedComment += code[n];
}
}
trimWhitespace(translatedComment);
// For block commands, the translator adds the newline after
// \endcode, so try and compensate by removing the last newline from
// the code text:
eraseTrailingSpaceNewLines(translatedComment);
translatedComment += "</code>";
translatedComment += "\n";
}
void CSharpDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
translatedComment += tag.data;
}
void CSharpDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
translatedComment += arg;
for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
translatedComment += it->data;
}
}
void CSharpDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
translatedComment += arg;
handleParagraph(tag, translatedComment);
translatedComment += "\">\n";
}
void CSharpDocConverter::handleTagSee(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
translatedComment += "<seealso cref=\"";
translatedComment += translateSubtree(tag);
eraseTrailingSpaceNewLines(translatedComment);
translatedComment += "\"/>\n";
}
void CSharpDocConverter::handleTagCharReplace(DoxygenEntity &, std::string &translatedComment, const std::string & arg) {
translatedComment += arg;
}
void CSharpDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
translatedComment += tag.typeOfEntity;
}
void CSharpDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
translatedComment += arg;
if (tag.entityList.size()) {
translatedComment += tag.entityList.begin()->data;
tag.entityList.pop_front();
translatedComment += " {" + translateSubtree(tag) + "}";
}
}
void CSharpDocConverter::handleTagWord(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
translatedComment += arg +": ";
if (tag.entityList.size())
translatedComment += tag.entityList.begin()->data;
tag.entityList.pop_front();
translatedComment += translateSubtree(tag);
translatedComment += "\n";
}
void CSharpDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
if (tag.entityList.size() < 2)
return;
tag.entityList.pop_front();
translatedComment += "Image: ";
translatedComment += tag.entityList.begin()->data;
tag.entityList.pop_front();
if (tag.entityList.size())
translatedComment += "(" + tag.entityList.begin()->data + ")";
}
void CSharpDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
if (tag.entityList.size() < 2)
return;
IndentGuard indent(translatedComment, m_indent);
DoxygenEntity paramNameEntity = *tag.entityList.begin();
tag.entityList.pop_front();
const std::string &paramName = paramNameEntity.data;
const std::string paramValue = getParamValue(paramName);
translatedComment += "<param name=\"" + paramName + "\">";
translatedComment += translateSubtree(tag);
eraseTrailingSpaceNewLines(translatedComment);
translatedComment += "</param> \n";
}
void CSharpDocConverter::handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
IndentGuard indent(translatedComment, m_indent);
translatedComment += "<returns>";
translatedComment += translateSubtree(tag);
eraseTrailingSpaceNewLines(translatedComment);
translatedComment += "</returns> \n";
}
void CSharpDocConverter::handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
IndentGuard indent(translatedComment, m_indent);
DoxygenEntity paramNameEntity = *tag.entityList.begin();
tag.entityList.pop_front();
const std::string &paramName = paramNameEntity.data;
const std::string paramType = getParamType(paramName);
const std::string paramValue = getParamValue(paramName);
translatedComment += "<exception cref=\"" + paramName + "\">";
translatedComment += translateSubtree(tag);
eraseTrailingSpaceNewLines(translatedComment);
translatedComment += "</exception> \n";
}
void CSharpDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
if (!tag.entityList.size())
return;
string anchor = tag.entityList.begin()->data;
tag.entityList.pop_front();
string anchorText = anchor;
size_t pos = anchorText.find('#');
if (pos != string::npos) {
anchorText = anchorText.substr(pos + 1);
}
if (!tag.entityList.empty()) {
anchorText = tag.entityList.begin()->data;
}
translatedComment += "\\ref " + anchorText;
}
void CSharpDocConverter::handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
if (tag.entityList.size()) { // do not include empty tags
std::string tagData = translateSubtree(tag);
// wrap the thing, ignoring whitespace
size_t wsPos = tagData.find_last_not_of("\n\t ");
if (wsPos != std::string::npos && wsPos != tagData.size() - 1)
translatedComment += arg + tagData.substr(0, wsPos + 1) + arg + tagData.substr(wsPos + 1);
else
translatedComment += arg + tagData + arg;
}
}
void CSharpDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
std::string htmlTagArgs = tag.data;
if (htmlTagArgs == "/") {
// end html tag, for example "</ul>
// translatedComment += "</" + arg.substr(1) + ">";
} else {
translatedComment += arg + htmlTagArgs;
}
}
void CSharpDocConverter::handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
std::string htmlTagArgs = tag.data;
if (htmlTagArgs == "/") {
// end html tag, for example "</ul>
} else {
translatedComment += arg;
}
}
void CSharpDocConverter::handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
std::string htmlTagArgs = tag.data;
if (htmlTagArgs == "/") {
// end html tag, "</a>
translatedComment += " (" + m_url + ')';
m_url.clear();
} else {
m_url.clear();
size_t pos = htmlTagArgs.find('=');
if (pos != string::npos) {
m_url = htmlTagArgs.substr(pos + 1);
}
translatedComment += arg;
}
}
void CSharpDocConverter::handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
std::string htmlTagArgs = tag.data;
if (htmlTagArgs == "/") {
// end html tag, for example "</em>
translatedComment += arg;
} else {
translatedComment += arg;
}
}
void CSharpDocConverter::handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
std::string htmlTagArgs = tag.data;
size_t nlPos = translatedComment.rfind('\n');
if (htmlTagArgs == "/") {
// end tag, </tr> appends vertical table line '|'
translatedComment += '|';
if (nlPos != string::npos) {
size_t startOfTableLinePos = translatedComment.find_first_not_of(" \t", nlPos + 1);
if (startOfTableLinePos != string::npos) {
m_tableLineLen = translatedComment.size() - startOfTableLinePos;
}
}
} else {
if (m_prevRowIsTH) {
// if previous row contained <th> tag, add horizontal separator
// but first get leading spaces, because they'll be needed for the next row
size_t numLeadingSpaces = translatedComment.size() - nlPos - 1;
translatedComment += string(m_tableLineLen, '-') + '\n';
if (nlPos != string::npos) {
translatedComment += string(numLeadingSpaces, ' ');
}
m_prevRowIsTH = false;
}
}
}
void CSharpDocConverter::handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
std::string htmlTagArgs = tag.data;
if (htmlTagArgs == "/") {
// end tag, </th> is ignored
} else {
translatedComment += '|';
m_prevRowIsTH = true;
}
}
void CSharpDocConverter::handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
std::string htmlTagArgs = tag.data;
if (htmlTagArgs == "/") {
// end tag, </td> is ignored
} else {
translatedComment += '|';
}
}
void CSharpDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg) {
// html entities
translatedComment += arg;
}
void CSharpDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, const std::string &) {
trimWhitespace(translatedComment);
translatedComment += "\n";
if (!m_indent.empty())
translatedComment += m_indent;
}
String *CSharpDocConverter::makeDocumentation(Node *n) {
String *documentation;
std::string csharpDocString;
// store the node, we may need it later
currentNode = n;
documentation = getDoxygenComment(n);
if (documentation != NULL) {
if (GetFlag(n, "feature:doxygen:notranslate")) {
String *comment = NewString("");
Append(comment, documentation);
Replaceall(comment, "\n *", "\n");
csharpDocString = Char(comment);
Delete(comment);
} else {
std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
DoxygenEntity root("root", entityList);
csharpDocString = translateSubtree(root);
}
}
// if we got something log the result
if (!csharpDocString.empty()) {
// remove the last spaces and '\n' since additional one is added during writing to file
eraseTrailingSpaceNewLines(csharpDocString);
// ensure that a blank line occurs before code or math blocks
csharpDocString = padCodeAndVerbatimBlocks(csharpDocString);
if (m_flags & debug_translator) {
std::cout << "\n---RESULT IN CSHARPDOC---" << std::endl;
std::cout << csharpDocString;
std::cout << std::endl;
}
}
return NewString(csharpDocString.c_str());
}

241
Source/Doxygen/csharpdoc.h Normal file
View File

@ -0,0 +1,241 @@
/* -----------------------------------------------------------------------------
* This file is part of SWIG, which is licensed as a whole under version 3
* (or any later version) of the GNU General Public License. Some additional
* terms also apply to certain portions of SWIG. The full details of the SWIG
* license and copyrights can be found in the LICENSE and COPYRIGHT files
* included with the SWIG source code as distributed by the SWIG developers
* and at http://www.swig.org/legal.html.
*
* csharpdoc.h
*
* Module to return documentation for nodes formatted for CSharp
* ----------------------------------------------------------------------------- */
#ifndef CSHARPDOCCONVERTER_H_
#define CSHARPDOCCONVERTER_H_
#include <list>
#include <string>
#include "swig.h"
#include "doxyentity.h"
#include "doxytranslator.h"
#define DOC_STRING_LENGTH 64 // characters per line allowed
#define DOC_PARAM_STRING_LENGTH 30 // characters reserved for param name / type
class CSharpDocConverter : public DoxygenTranslator {
public:
CSharpDocConverter(int flags = 0);
String *makeDocumentation(Node *node);
protected:
size_t m_tableLineLen;
bool m_prevRowIsTH;
std::string m_url;
/*
* Translate every entity in a tree, also manages sections
* display. Prints title for every group of tags that have
* a section title associated with them
*/
std::string translateSubtree(DoxygenEntity &doxygenEntity);
/*
* Translate one entity with the appropriate handler, according
* to the tagHandlers
*/
void translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment);
/*
* Typedef for the function that handles one tag
* arg - some string argument to easily pass it through lookup table
*/
typedef void (CSharpDocConverter::*tagHandler) (DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Wrap the command data with the some string
* arg - string to wrap with, like '_' or '*'
*/
void handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Just prints new line
*/
void handleNewLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Replace char by the one ine argument
*/
void handleTagCharReplace(DoxygenEntity &tag, std::string &translatedComment, const std::string & arg);
/*
* Print the name of tag to the output, used for escape-commands
*/
void handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Format the contents of the \exception tag or its synonyms.
*/
void handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/**
* Print the content without any tag
*/
void handleNotHandled(DoxygenEntity &tag, std::string &translatedComment, const std::string & tagName);
/**
* Print the content as an item of a list
*/
void handleAddList(DoxygenEntity &tag, std::string &translatedComment, const std::string & tagName);
/*
* Print only the content and strip original tag
*/
void handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
/*
* Ignore the tag
*/
void handleIgnore(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
/*
* Print only the line content and strip original tag
*/
void handleLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
/*
* Print summary
*/
void handleSummary(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
/*
* Handle Doxygen verbatim tag
*/
void handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
/*
* Handle one of the Doxygen formula-related tags.
*/
void handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Handle a code snippet.
*/
void handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Print only data part of code
*/
void handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/**
* Copies verbatim args of the tag to output, used for commands like \f$, ...
*/
void handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Print the if-elseif-else-endif section
*/
void handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Prints the specified message, than the contents of the tag
* arg - message
*/
void handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Prints the seealso tag
*/
void handleTagSee(DoxygenEntity &tag, std::string &translatedComment, const std::string &);
/*
* Insert 'Word: ...'
*/
void handleTagWord(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Insert 'Image: ...'
*/
void handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Format nice param description with type information
*/
void handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Format the contents of the \return tag or its synonyms.
*/
void handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Writes text for \ref tag.
*/
void handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/* Handles HTML tags recognized by Doxygen, like <A ...>, <ul>, <table>, ... */
void handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/** Does not output params of HTML tag, for example in <table border='1'>
* 'border=1' is not written to output.
*/
void handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/** Translates tag <a href = "url">text</a> to: text ("url"). */
void handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/*
* Handles HTML tags, which are translated to markdown-like syntax, for example
* <i>text</i> --> _text_. Appends arg for start HTML tag and end HTML tag.
*/
void handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/* Handles HTML table, tag <tr> */
void handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/* Handles HTML table, tag <th> */
void handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/* Handles HTML table, tag <td> */
void handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
/* Handles HTML entities recognized by Doxygen, like &lt;, &copy;, ... */
void handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg);
/*
* Simple helper function that calculates correct parameter type
* of the node stored in 'currentNode'
* If param with specified name is not found, empty string is returned
*/
std::string getParamType(std::string name);
/*
* Simple helper function to retrieve the parameter value
*/
std::string getParamValue(std::string name);
private:
// temporary thing, should be refactored somehow
Node *currentNode;
// Extra indent for the current paragraph, must be output after each new line.
std::string m_indent;
// this contains the handler pointer and one string argument
typedef std::map<std::string, std::pair<tagHandler, std::string> >TagHandlersMap;
static TagHandlersMap tagHandlers;
// Helper functions for fillStaticTables(): make a new tag handler object.
TagHandlersMap::mapped_type make_handler(tagHandler handler);
TagHandlersMap::mapped_type make_handler(tagHandler handler, const char *arg);
void fillStaticTables();
};
#endif

View File

@ -34,6 +34,8 @@ eswig_SOURCES = CParse/cscanner.c \
DOH/memory.c \
DOH/string.c \
DOH/void.c \
Doxygen/csharpdoc.cxx \
Doxygen/csharpdoc.h \
Doxygen/doxyentity.cxx \
Doxygen/doxyentity.h \
Doxygen/doxyparser.cxx \

View File

@ -15,6 +15,7 @@
#include "cparse.h"
#include <limits.h> // for INT_MAX
#include <ctype.h>
#include <csharpdoc.h>
/* Hash type used for upcalls from C/C++ */
typedef DOH UpcallData;
@ -46,6 +47,7 @@ class CSHARP:public Language {
bool global_variable_flag; // Flag for when wrapping a global variable
bool old_variable_names; // Flag for old style variable names in the intermediary class
bool generate_property_declaration_flag; // Flag for generating properties
bool doxygen; // Flag for Doxygen generation
String *imclass_name; // intermediary class name
String *module_class_name; // module class name
@ -123,6 +125,7 @@ public:
global_variable_flag(false),
old_variable_names(false),
generate_property_declaration_flag(false),
doxygen(false),
imclass_name(NULL),
module_class_name(NULL),
imclass_class_code(NULL),
@ -171,6 +174,14 @@ public:
directorLanguage();
}
/* -----------------------------------------------------------------------------
* ~CSHARP()
* ----------------------------------------------------------------------------- */
~CSHARP()
{
delete doxygenTranslator;
}
/* -----------------------------------------------------------------------------
* getProxyName()
*
@ -223,6 +234,8 @@ public:
SWIG_library_directory("csharp");
int doxygen_translator_flags = 0;
// Look for certain command line options
for (int i = 1; i < argc; i++) {
if (argv[i]) {
@ -265,13 +278,28 @@ public:
i++;
} else {
Swig_arg_error();
}
} else if (strcmp(argv[i], "-help") == 0) {
Printf(stdout, "%s\n", usage);
}
}
} else if (strcmp(argv[i], "-doxygen") == 0)
{
doxygen = true;
scan_doxygen_comments = 1;
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-debug-doxygen-translator") == 0) {
doxygen_translator_flags |= DoxygenTranslator::debug_translator;
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-debug-doxygen-parser") == 0) {
doxygen_translator_flags |= DoxygenTranslator::debug_parser;
Swig_mark_arg(i);
} else if (strcmp(argv[i], "-help") == 0) {
Printf(stdout, "%s\n", usage);
}
}
}
if (doxygen)
doxygenTranslator = new CSharpDocConverter(doxygen_translator_flags);
// Add a symbol to the parser for conditional compilation
Preprocessor_define("SWIGCSHARP 1", 0);
@ -439,7 +467,7 @@ public:
}
if (old_variable_names) {
Swig_name_register("set", "set_%n%v");
Swig_name_register("/*3/ set", "set_%n%v");
Swig_name_register("get", "get_%n%v");
}
@ -1077,6 +1105,7 @@ public:
if (!(proxy_flag && is_wrapping_class()) && !enum_constant_flag) {
moduleClassFunctionHandler(n);
}
/*
* Generate the proxy class properties for public member variables.
@ -1087,8 +1116,10 @@ public:
bool getter_flag = Cmp(symname, Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) != 0;
String *getter_setter_name = NewString("");
if (!getter_flag)
Printf(getter_setter_name, "set");
Printf(getter_setter_name, "/*1/set");
else
Printf(getter_setter_name, "get");
Putc(toupper((int) *Char(variable_name)), getter_setter_name);
@ -1186,6 +1217,11 @@ public:
EnumFeature enum_feature = decodeEnumFeature(n);
String *typemap_lookup_type = Getattr(n, "name");
// Translate and write comment if flagged
if (have_docstring(n)) {
Printv(enum_code, docstring(n, tab4, true), NIL, NIL);
}
if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
// Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper C# enum
@ -1256,6 +1292,7 @@ public:
Delete(full_imclass_name);
full_imclass_name = 0;
}
if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
// Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper C# enum
@ -1391,6 +1428,10 @@ public:
if (csattributes)
Printf(enum_code, " %s\n", csattributes);
// Translate and write comment if flagged
if (have_docstring(n)) {
Printv(enum_code, docstring(n, tab4, true), NIL, NIL);
}
Printf(enum_code, " %s", symname);
// Check for the %csconstvalue feature
@ -1419,10 +1460,10 @@ public:
// Wrap (non-anonymous) enum using the typesafe enum pattern
if (Getattr(n, "enumvalue")) {
String *value = enumValue(n);
Printf(enum_code, " %s static readonly %s %s = new %s(\"%s\", %s);\n", methodmods, return_type, symname, return_type, symname, value);
Printf(enum_code, " %s /*1*/static readonly %s %s = new %s(\"%s\", %s);\n", methodmods, return_type, symname, return_type, symname, value);
Delete(value);
} else {
Printf(enum_code, " %s static readonly %s %s = new %s(\"%s\");\n", methodmods, return_type, symname, return_type, symname);
} else {
Printf(enum_code, " %s /*2*/static readonly %s %s = new %s(\"%s\");\n", methodmods, return_type, symname, return_type, symname);
}
} else {
// Simple integer constants
@ -1431,8 +1472,7 @@ public:
// The %csconst feature determines how the constant value is obtained
int const_feature_flag = GetFlag(n, "feature:cs:const");
const char *const_readonly = const_feature_flag ? "const" : "static readonly";
const char *const_readonly = const_feature_flag ? "const" : "static readonly";
String *value = enumValue(n);
Printf(enum_code, " %s %s %s %s = %s;\n", methodmods, const_readonly, return_type, symname, value);
Delete(value);
@ -1541,6 +1581,11 @@ public:
const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
// Translate and write comment if flagged
if (have_docstring(n)) {
Printv(constants_code, docstring(n, tab4, true), NIL, NIL);
}
Printf(constants_code, " %s %s %s %s = ", methodmods, (const_feature_flag ? "const" : "static readonly"), return_type, itemname);
// Check for the %csconstvalue feature
@ -2214,6 +2259,11 @@ public:
Language::classHandler(n);
// Translate and write comment if flagged
if (have_docstring(n)) {
Printv(proxy_class_def, docstring(n, tab4, true), NIL);
}
if (proxy_flag) {
emitProxyClassDefAndCPPCasts(n);
@ -2367,6 +2417,11 @@ public:
}
static_flag = false;
// NO EFFECT
if (have_docstring(n)) {
Printv(module_class_code, docstring(n, tab4, true), NIL);
}
return SWIG_OK;
}
@ -2391,6 +2446,7 @@ public:
Parm *p;
Parm *last_parm = 0;
int i;
String* comment_code = NewString("");
String *imcall = NewString("");
String *return_type = NewString("");
String *function_code = NewString("");
@ -2449,6 +2505,8 @@ public:
Swig_typemap_attach_parms("csvarin", l, NULL);
}
/* Start generating the proxy function */
const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
if (outattributes)
@ -2488,7 +2546,6 @@ public:
Printf(function_code, "%s %s(", return_type, proxy_function_name);
if (is_interface)
Printf(interface_class_code, " %s %s(", return_type, proxy_function_name);
Printv(imcall, full_imclass_name, ".$imfuncname(", NIL);
if (!static_flag)
@ -2588,6 +2645,12 @@ public:
if (is_interface)
Printf(interface_class_code, ");\n");
// Translate and write comment if flagged
if (have_docstring(n))
{
Printv(comment_code, docstring(n, tab4, true), NIL);
}
// Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in proxy class)
if ((tm = Swig_typemap_lookup("csout", n, "", 0))) {
excodeSubstitute(n, tm, "csout", n);
@ -2682,8 +2745,14 @@ public:
if (!methodmods)
methodmods = (is_public(n) ? public_string : protected_string);
// Translate and write comment if flagged
if (have_docstring(n))
{
Printv(proxy_class_code, docstring(n, tab4, true), NIL);
}
// Start property declaration
Printf(proxy_class_code, " %s %s%s %s {", methodmods, static_flag ? "static " : "", variable_type, variable_name);
Printf(proxy_class_code, "%s %s%s %s {", methodmods, static_flag ? "static " : "", variable_type, variable_name);
}
generate_property_declaration_flag = false;
@ -2721,6 +2790,7 @@ public:
} else {
// Normal function call
Printf(function_code, " %s\n\n", tm ? (const String *) tm : empty_string);
Printv(proxy_class_code, comment_code, NIL);
Printv(proxy_class_code, function_code, NIL);
}
@ -2743,6 +2813,7 @@ public:
Parm *p;
int i;
String *function_code = NewString("");
String* comment_code = NewString("");
String *helper_code = NewString(""); // Holds code for the constructor helper method generated only when the csin typemap has code in the pre or post attributes
String *helper_args = NewString("");
String *pre_code = NewString("");
@ -2938,7 +3009,14 @@ public:
} else {
Replaceall(function_code, "$imcall", imcall);
}
// Translate and write comment if flagged
if (have_docstring(n))
{
Printv(comment_code, docstring(n, tab4, true), NIL);
}
Printv(proxy_class_code, comment_code, NIL);
Printv(proxy_class_code, function_code, "\n", NIL);
Delete(helper_args);
@ -3024,8 +3102,10 @@ public:
virtual int memberconstantHandler(Node *n) {
variable_name = Getattr(n, "sym:name");
wrapping_member_flag = true;
Language::memberconstantHandler(n);
wrapping_member_flag = false;
return SWIG_OK;
}
@ -3057,6 +3137,7 @@ public:
String *tm;
Parm *p;
Parm *last_parm = 0;
String* comment_code = NewString("");
int i;
String *imcall = NewString("");
String *return_type = NewString("");
@ -3095,11 +3176,14 @@ public:
/* Change function name for global variables */
if (proxy_flag && global_variable_flag) {
// Capitalize the first letter in the variable to create the getter/setter function name
func_name = NewString("");
setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), variable_name)) == 0);
if (setter_flag)
Printf(func_name, "set");
Printf(func_name, "/*2*/set");
else
Printf(func_name, "get");
Putc(toupper((int) *Char(variable_name)), func_name);
@ -3119,6 +3203,12 @@ public:
Printf(function_code, " %s\n", csattributes);
const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
// Translate and write comment if flagged
if (have_docstring(n))
Printv(function_code, docstring(n, tab4, true), NIL, NIL);
Printf(function_code, " %s static %s %s(", methodmods, return_type, func_name);
Printv(imcall, imclass_name, ".", overloaded_name, "(", NIL);
@ -4652,6 +4742,155 @@ public:
Delete(dirclassname);
}
/* ------------------------------------------------------------
* have_docstring()
*
* Check if there is a docstring directive and it has text,
* or there is an autodoc flag set
* ------------------------------------------------------------ */
bool have_docstring(Node* n) {
String* str = Getattr(n, "feature:docstring");
return ((str && Len(str) > 0)
|| (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"))
|| (doxygen && doxygenTranslator->hasDocumentation(n))
);
}
/* ------------------------------------------------------------
* docstring()
*
* Get the docstring text, stripping off {} if necessary,
* and enclose in triple double quotes. If autodoc is also
* set then it will build a combined docstring.
* ------------------------------------------------------------ */
String* docstring(Node* n, const String* indent, bool low_level = false) {
String* docstr = build_combined_docstring(n, indent, low_level);
const int len = Len(docstr);
if (!len)
return docstr;
return docstr;
}
/* ------------------------------------------------------------
* build_combined_docstring()
*
* Build the full docstring which may be a combination of the
* explicit docstring and autodoc string or, if none of them
* is specified, obtained by translating Doxygen comment to
* Python.
*
* Return new string to be deleted by caller (never NIL but
* may be empty if there is no docstring).
* ------------------------------------------------------------ */
String* build_combined_docstring(Node* n, const String* indent = "", bool low_level = false) {
String* docstr = NULL;
if (doxygen && doxygenTranslator->hasDocumentation(n)) {
docstr = doxygenTranslator->getDocumentation(n, 0);
}
if (!docstr)
docstr = NewString("");
// If there is more than one line then make docstrings like this:
//
// This is line1
// And here is line2 followed by the rest of them
//
// otherwise, put it all on a single line
if (Strchr(docstr, '\n')) {
String* tmp = NewString("");
Append(tmp, indent_docstring(docstr, indent));
Append(tmp, indent);
Delete(docstr);
docstr = tmp;
}
else
{
String* tmp = NewString("");
Append(tmp, "/// ");
Append(tmp, docstr);
Append(tmp, "\n");
Delete(docstr);
docstr = tmp;
}
return docstr;
}
/* ------------------------------------------------------------
* indent_docstring()
*
* Format (indent) a CSharp docstring.
* Remove leading whitespace from 'code' and re-indent using
* the indentation string in 'indent'.
* ------------------------------------------------------------ */
String* indent_docstring(const String* code, const_String_or_char_ptr indent) {
String* out = NewString("");
String* temp;
char* t;
if (!indent)
indent = "";
temp = NewString(code);
t = Char(temp);
if (*t == '{') {
Delitem(temp, 0);
Delitem(temp, DOH_END);
}
/* Split the input text into lines */
List* clist = SplitLines(temp);
Delete(temp);
Iterator si;
int truncate_characters_count = INT_MAX;
for (si = First(clist); si.item; si = Next(si)) {
const char* c = Char(si.item);
int i;
for (i = 0; isspace((unsigned char)c[i]); i++) {
// Scan forward until we find a non-space (which may be a null byte).
}
char ch = c[i];
if (ch) {
// Found a line which isn't just whitespace
if (i < truncate_characters_count)
truncate_characters_count = i;
}
}
if (truncate_characters_count == INT_MAX)
truncate_characters_count = 0;
for (si = First(clist); si.item; si = Next(si)) {
const char* c = Char(si.item);
int i;
for (i = 0; isspace((unsigned char)c[i]); i++) {
// Scan forward until we find a non-space (which may be a null byte).
}
char ch = c[i];
if (!ch) {
// Line is just whitespace - emit an empty line.
Printv(out, indent, "///", NIL, NIL);
Putc('\n', out);
continue;
}
Printv(out, indent, "/// ", c + truncate_characters_count, "\n", NIL);
}
Delete(clist);
return out;
}
/*----------------------------------------------------------------------
* nestedClassesSupport()
*--------------------------------------------------------------------*/