summaryrefslogtreecommitdiffstats
path: root/contrib/tools/swig/Source/Doxygen/pydoc.cxx
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-08-28 14:27:58 +0300
committerrobot-piglet <[email protected]>2025-08-28 14:57:06 +0300
commit81d828c32c8d5477cb2f0ce5da06a1a8d9392ca3 (patch)
tree3081d566f0d5158d76e9093261344f6406fd09f7 /contrib/tools/swig/Source/Doxygen/pydoc.cxx
parent77ea11423f959e51795cc3ef36a48d808b4ffb98 (diff)
Intermediate changes
commit_hash:d5b1af16dbe9030537a04c27eb410c88c2f496cd
Diffstat (limited to 'contrib/tools/swig/Source/Doxygen/pydoc.cxx')
-rw-r--r--contrib/tools/swig/Source/Doxygen/pydoc.cxx993
1 files changed, 993 insertions, 0 deletions
diff --git a/contrib/tools/swig/Source/Doxygen/pydoc.cxx b/contrib/tools/swig/Source/Doxygen/pydoc.cxx
new file mode 100644
index 00000000000..03986a49c9a
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/pydoc.cxx
@@ -0,0 +1,993 @@
+/* -----------------------------------------------------------------------------
+ * 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 https://www.swig.org/legal.html.
+ *
+ * pydoc.cxx
+ *
+ * Module to return documentation for nodes formatted for PyDoc
+ * ----------------------------------------------------------------------------- */
+
+#include "pydoc.h"
+#include "doxyparser.h"
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "swigmod.h"
+
+// define static tables, they are filled in PyDocConverter's constructor
+PyDocConverter::TagHandlersMap PyDocConverter::tagHandlers;
+std::map<std::string, std::string> PyDocConverter::sectionTitles;
+
+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 &);
+ IndentGuard &operator=(const IndentGuard &);
+};
+
+// Return the indent of the given multiline string, i.e. the maximal number of
+// spaces present in the beginning of all its non-empty lines.
+static size_t determineIndent(const string &s) {
+ size_t minIndent = static_cast<size_t>(-1);
+
+ for (size_t lineStart = 0; lineStart < s.length();) {
+ const size_t lineEnd = s.find('\n', lineStart);
+ const size_t firstNonSpace = s.find_first_not_of(' ', lineStart);
+
+ // If inequality doesn't hold, it means that this line contains only spaces
+ // (notice that this works whether lineEnd is valid or string::npos), in
+ // which case it doesn't matter when determining the indent.
+ if (firstNonSpace < lineEnd) {
+ // Here we can be sure firstNonSpace != string::npos.
+ const size_t lineIndent = firstNonSpace - lineStart;
+ if (lineIndent < minIndent)
+ minIndent = lineIndent;
+ }
+
+ if (lineEnd == string::npos)
+ break;
+
+ lineStart = lineEnd + 1;
+ }
+
+ return minIndent;
+}
+
+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 last character in the string if it is a newline
+static void eraseTrailingNewLine(string &s) {
+ if (!s.empty() && s[s.size() - 1] == '\n')
+ s.erase(s.size() - 1);
+}
+
+// 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 */
+PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler) {
+ return make_pair(handler, std::string());
+}
+
+/* static */
+PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler, const char *arg) {
+ return make_pair(handler, arg);
+}
+
+void PyDocConverter::fillStaticTables() {
+ if (tagHandlers.size()) // fill only once
+ return;
+
+ // table of section titles, they are printed only once
+ // for each group of specified doxygen commands
+ sectionTitles["author"] = "Author: ";
+ sectionTitles["authors"] = "Authors: ";
+ sectionTitles["copyright"] = "Copyright: ";
+ sectionTitles["deprecated"] = "Deprecated: ";
+ sectionTitles["example"] = "Example: ";
+ sectionTitles["note"] = "Notes: ";
+ sectionTitles["remark"] = "Remarks: ";
+ sectionTitles["remarks"] = "Remarks: ";
+ sectionTitles["warning"] = "Warning: ";
+// sectionTitles["sa"] = "See also: ";
+// sectionTitles["see"] = "See also: ";
+ sectionTitles["since"] = "Since: ";
+ sectionTitles["todo"] = "TODO: ";
+ sectionTitles["version"] = "Version: ";
+
+ tagHandlers["a"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ tagHandlers["b"] = make_handler(&PyDocConverter::handleTagWrap, "**");
+ // \c command is translated as single quotes around next word
+ tagHandlers["c"] = make_handler(&PyDocConverter::handleTagWrap, "``");
+ tagHandlers["cite"] = make_handler(&PyDocConverter::handleTagWrap, "'");
+ tagHandlers["e"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ // these commands insert just a single char, some of them need to be escaped
+ tagHandlers["$"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["@"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["\\"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["<"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers[">"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["&"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["#"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["%"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["~"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["\""] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["."] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["::"] = make_handler(&PyDocConverter::handleTagChar);
+ // these commands are stripped out, and only their content is printed
+ tagHandlers["attention"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["author"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["authors"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["brief"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["bug"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["code"] = make_handler(&PyDocConverter::handleCode);
+ tagHandlers["copyright"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["date"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["deprecated"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["details"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["em"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ tagHandlers["example"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["exception"] = tagHandlers["throw"] = tagHandlers["throws"] = make_handler(&PyDocConverter::handleTagException);
+ tagHandlers["htmlonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["invariant"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["latexonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["link"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["manonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["note"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["p"] = make_handler(&PyDocConverter::handleTagWrap, "``");
+ tagHandlers["partofdescription"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["rtfonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["remark"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["remarks"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["sa"] = make_handler(&PyDocConverter::handleTagMessage, "See also: ");
+ tagHandlers["see"] = make_handler(&PyDocConverter::handleTagMessage, "See also: ");
+ tagHandlers["since"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["short"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["todo"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["version"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["verbatim"] = make_handler(&PyDocConverter::handleVerbatimBlock);
+ tagHandlers["warning"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["xmlonly"] = make_handler(&PyDocConverter::handleParagraph);
+ // these commands have special handlers
+ tagHandlers["arg"] = make_handler(&PyDocConverter::handleTagMessage, "* ");
+ tagHandlers["cond"] = make_handler(&PyDocConverter::handleTagMessage, "Conditional comment: ");
+ tagHandlers["else"] = make_handler(&PyDocConverter::handleTagIf, "Else: ");
+ tagHandlers["elseif"] = make_handler(&PyDocConverter::handleTagIf, "Else if: ");
+ tagHandlers["endcond"] = make_handler(&PyDocConverter::handleTagMessage, "End of conditional comment.");
+ tagHandlers["if"] = make_handler(&PyDocConverter::handleTagIf, "If: ");
+ tagHandlers["ifnot"] = make_handler(&PyDocConverter::handleTagIf, "If not: ");
+ tagHandlers["image"] = make_handler(&PyDocConverter::handleTagImage);
+ tagHandlers["li"] = make_handler(&PyDocConverter::handleTagMessage, "* ");
+ tagHandlers["overload"] = make_handler(&PyDocConverter::handleTagMessage,
+ "This is an overloaded member function, provided for"
+ " convenience.\nIt differs from the above function only in what" " argument(s) it accepts.");
+ tagHandlers["par"] = make_handler(&PyDocConverter::handleTagPar);
+ tagHandlers["param"] = tagHandlers["tparam"] = make_handler(&PyDocConverter::handleTagParam);
+ tagHandlers["ref"] = make_handler(&PyDocConverter::handleTagRef);
+ tagHandlers["result"] = tagHandlers["return"] = tagHandlers["returns"] = make_handler(&PyDocConverter::handleTagReturn);
+
+ // this command just prints its contents
+ // (it is internal command of swig's parser, contains plain text)
+ tagHandlers["plainstd::string"] = make_handler(&PyDocConverter::handlePlainString);
+ tagHandlers["plainstd::endl"] = make_handler(&PyDocConverter::handleNewLine);
+ tagHandlers["n"] = make_handler(&PyDocConverter::handleNewLine);
+
+ // \f commands output literal Latex formula, which is still better than nothing.
+ tagHandlers["f$"] = tagHandlers["f["] = tagHandlers["f{"] = make_handler(&PyDocConverter::handleMath);
+
+ // HTML tags
+ tagHandlers["<a"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_A);
+ tagHandlers["<b"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<blockquote"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_A, "Quote: ");
+ tagHandlers["<body"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<br"] = make_handler(&PyDocConverter::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(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<caption"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<code"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "``");
+
+ tagHandlers["<dl"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<dd"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<dt"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+
+ tagHandlers["<dfn"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<div"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<em"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<form"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<hr"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "--------------------------------------------------------------------\n");
+ tagHandlers["<h1"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "# ");
+ tagHandlers["<h2"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "## ");
+ tagHandlers["<h3"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "### ");
+ tagHandlers["<i"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "*");
+ tagHandlers["<input"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<img"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "Image:");
+ tagHandlers["<li"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "* ");
+ tagHandlers["<meta"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<multicol"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ol"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<p"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "\n");
+ tagHandlers["<pre"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<small"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<span"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "'");
+ tagHandlers["<strong"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+
+ // make a space between text and super/sub script.
+ tagHandlers["<sub"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<sup"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+
+ tagHandlers["<table"] = make_handler(&PyDocConverter::handleDoxyHtmlTagNoParam);
+ tagHandlers["<td"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_td);
+ tagHandlers["<th"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_th);
+ tagHandlers["<tr"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_tr);
+ tagHandlers["<tt"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<kbd"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ul"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<var"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "*");
+
+ // HTML entities
+ tagHandlers["&copy"] = make_handler(&PyDocConverter::handleHtmlEntity, "(C)");
+ tagHandlers["&trade"] = make_handler(&PyDocConverter::handleHtmlEntity, " TM");
+ tagHandlers["&reg"] = make_handler(&PyDocConverter::handleHtmlEntity, "(R)");
+ tagHandlers["&lt"] = make_handler(&PyDocConverter::handleHtmlEntity, "<");
+ tagHandlers["&gt"] = make_handler(&PyDocConverter::handleHtmlEntity, ">");
+ tagHandlers["&amp"] = make_handler(&PyDocConverter::handleHtmlEntity, "&");
+ tagHandlers["&apos"] = make_handler(&PyDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&quot"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&lsquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "`");
+ tagHandlers["&rsquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&ldquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&rdquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&ndash"] = make_handler(&PyDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&mdash"] = make_handler(&PyDocConverter::handleHtmlEntity, "--");
+ tagHandlers["&nbsp"] = make_handler(&PyDocConverter::handleHtmlEntity, " ");
+ tagHandlers["&times"] = make_handler(&PyDocConverter::handleHtmlEntity, "x");
+ tagHandlers["&minus"] = make_handler(&PyDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&sdot"] = make_handler(&PyDocConverter::handleHtmlEntity, ".");
+ tagHandlers["&sim"] = make_handler(&PyDocConverter::handleHtmlEntity, "~");
+ tagHandlers["&le"] = make_handler(&PyDocConverter::handleHtmlEntity, "<=");
+ tagHandlers["&ge"] = make_handler(&PyDocConverter::handleHtmlEntity, ">=");
+ tagHandlers["&larr"] = make_handler(&PyDocConverter::handleHtmlEntity, "<--");
+ tagHandlers["&rarr"] = make_handler(&PyDocConverter::handleHtmlEntity, "-->");
+}
+
+PyDocConverter::PyDocConverter(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 getPyDocType(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, NULL);
+ }
+
+ if (!s)
+ return type;
+
+ if (Language::classLookup(s)) {
+ // In Python C++ namespaces are flattened, so remove all but last component
+ // of the name.
+ String *const last = Swig_scopename_last(s);
+
+ // We are not actually sure whether it's a documented class or not, but
+ // there doesn't seem to be any harm in making it a reference if it isn't,
+ // while there is a lot of benefit in having a hyperlink if it is.
+ type = ":py:class:`";
+ type += Char(last);
+ type += "`";
+
+ Delete(last);
+ } else {
+ type = Char(s);
+ }
+
+ Delete(s);
+
+ return type;
+}
+
+std::string PyDocConverter::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 = getPyDocType(p, pname);
+ break;
+ }
+ }
+ Delete(plist);
+ return type;
+}
+
+std::string PyDocConverter::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 PyDocConverter::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()) {
+ std::map<std::string, std::string>::iterator it;
+ it = sectionTitles.find(p->typeOfEntity);
+ if (it != sectionTitles.end()) {
+ if (it->second != currentSection) {
+ currentSection = it->second;
+ translatedComment += currentSection;
+ }
+ }
+ translateEntity(*p, translatedComment);
+ translateSubtree(*p);
+ p++;
+ }
+
+ return translatedComment;
+}
+
+void PyDocConverter::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 PyDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += translateSubtree(tag);
+}
+
+void PyDocConverter::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
+ eraseTrailingNewLine(verb);
+ translatedComment += verb;
+}
+
+void PyDocConverter::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 PyDocConverter::handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ // Check for an option given to the code command (e.g. code{.py}),
+ // and try to set the code-block language accordingly.
+ string option = getCommandOption(tag.typeOfEntity, '{', '}');
+ // Set up the language option to the code-block command, which can
+ // be any language supported by pygments:
+ string codeLanguage;
+ if (option == ".py")
+ // Other possibilities here are "default" or "python3". In Sphinx
+ // 2.1.2, basic syntax doesn't render quite the same in these as
+ // with "python", which for basic keywords seems to provide
+ // slightly richer formatting. Another option would be to leave
+ // the language empty, but testing with Sphinx 1.8.5 has produced
+ // an error "1 argument required".
+ codeLanguage = "python";
+ else if (option == ".java")
+ codeLanguage = "java";
+ else if (option == ".c")
+ codeLanguage = "c";
+ else
+ // If there is not a match, or if no option was given, go out on a
+ // limb and assume that the examples in the C or C++ sources use
+ // C++.
+ codeLanguage = "c++";
+
+ std::string code;
+ handleTagVerbatim(tag, code, arg);
+
+ // Try and remove leading newline, which is present for block \code
+ // command:
+ eraseLeadingNewLine(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();
+ translatedComment += ".. code-block:: " + codeLanguage + "\n\n";
+
+ // 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:
+ eraseTrailingNewLine(translatedComment);
+}
+
+void PyDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.data;
+}
+
+void PyDocConverter::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 PyDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ handleParagraph(tag, translatedComment);
+}
+
+void PyDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.typeOfEntity;
+}
+
+void PyDocConverter::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 PyDocConverter::handleTagPar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += "Title: ";
+ if (tag.entityList.size())
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment);
+}
+
+void PyDocConverter::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 PyDocConverter::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 paramType = getParamType(paramName);
+ const std::string paramValue = getParamValue(paramName);
+
+ // Get command option, e.g. "in", "out", or "in,out"
+ string commandOpt = getCommandOption(tag.typeOfEntity, '[', ']');
+ if (commandOpt == "in,out") commandOpt = "in/out";
+
+ // If provided, append the parameter direction to the type
+ // information via a suffix:
+ std::string suffix;
+ if (commandOpt.size() > 0)
+ suffix = ", " + commandOpt;
+
+ // If the parameter has a default value, flag it as optional in the
+ // generated type definition. Particularly helpful when the python
+ // call is generated with *args, **kwargs.
+ if (paramValue.size() > 0)
+ suffix += ", optional";
+
+ if (!paramType.empty()) {
+ translatedComment += ":type " + paramName + ": " + paramType + suffix + "\n";
+ translatedComment += indent.getFirstLineIndent();
+ }
+
+ translatedComment += ":param " + paramName + ":";
+
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ const std::string pytype = getPyDocType(currentNode);
+ if (!pytype.empty()) {
+ translatedComment += ":rtype: ";
+ translatedComment += pytype;
+ translatedComment += "\n";
+ translatedComment += indent.getFirstLineIndent();
+ }
+
+ translatedComment += ":return: ";
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ translatedComment += ":raises: ";
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::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;
+ if (!tag.entityList.empty()) {
+ anchorText = tag.entityList.begin()->data;
+ }
+ translatedComment += "'" + anchorText + "'";
+}
+
+
+void PyDocConverter::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 PyDocConverter::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 PyDocConverter::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 PyDocConverter::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 PyDocConverter::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 PyDocConverter::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 PyDocConverter::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 PyDocConverter::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 PyDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg) {
+ // html entities
+ translatedComment += arg;
+}
+
+void PyDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, const std::string &) {
+ trimWhitespace(translatedComment);
+
+ translatedComment += "\n";
+ if (!m_indent.empty())
+ translatedComment += m_indent;
+}
+
+String *PyDocConverter::makeDocumentation(Node *n) {
+ String *documentation;
+ std::string pyDocString;
+
+ // store the node, we may need it later
+ currentNode = n;
+
+ // for overloaded functions we must concat documentation for underlying overloads
+ if (Getattr(n, "sym:overloaded")) {
+ // rewind to the first overload
+ while (Getattr(n, "sym:previousSibling"))
+ n = Getattr(n, "sym:previousSibling");
+
+ std::vector<std::string> allDocumentation;
+
+ // minimal indent of any documentation comments, not initialized yet
+ size_t minIndent = static_cast<size_t>(-1);
+
+ // for each real method (not a generated overload) append the documentation
+ string oneDoc;
+ while (n) {
+ documentation = getDoxygenComment(n);
+ if (!Swig_is_generated_overload(n) && documentation) {
+ currentNode = n;
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ oneDoc = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+
+ oneDoc = translateSubtree(root);
+ }
+
+ // find the minimal indent of this documentation comment, we need to
+ // ensure that the entire comment is indented by it to avoid the leading
+ // parts of the other lines being simply discarded later
+ const size_t oneIndent = determineIndent(oneDoc);
+ if (oneIndent < minIndent)
+ minIndent = oneIndent;
+
+ allDocumentation.push_back(oneDoc);
+ }
+ n = Getattr(n, "sym:nextSibling");
+ }
+
+ // construct final documentation string
+ if (allDocumentation.size() > 1) {
+ string indentStr;
+ if (minIndent != static_cast<size_t>(-1))
+ indentStr.assign(minIndent, ' ');
+
+ std::ostringstream concatDocString;
+ for (size_t realOverloadCount = 0; realOverloadCount < allDocumentation.size(); realOverloadCount++) {
+ if (realOverloadCount != 0) {
+ // separate it from the preceding one.
+ concatDocString << '\n' << indentStr << "|\n\n";
+ }
+
+ oneDoc = allDocumentation[realOverloadCount];
+ trimWhitespace(oneDoc);
+ concatDocString << indentStr << "*Overload " << (realOverloadCount + 1) << ":*\n" << oneDoc;
+ }
+ pyDocString = concatDocString.str();
+ } else if (allDocumentation.size() == 1) {
+ pyDocString = *(allDocumentation.begin());
+ }
+ }
+ // for other nodes just process as normal
+ else {
+ documentation = getDoxygenComment(n);
+ if (documentation != NULL) {
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ pyDocString = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+ pyDocString = translateSubtree(root);
+ }
+ }
+ }
+
+ // if we got something log the result
+ if (!pyDocString.empty()) {
+
+ // remove the last '\n' since additional one is added during writing to file
+ eraseTrailingNewLine(pyDocString);
+
+ // ensure that a blank line occurs before code or math blocks
+ pyDocString = padCodeAndVerbatimBlocks(pyDocString);
+
+ if (m_flags & debug_translator) {
+ std::cout << "\n---RESULT IN PYDOC---" << std::endl;
+ std::cout << pyDocString;
+ std::cout << std::endl;
+ }
+ }
+
+ return NewString(pyDocString.c_str());
+}
+