From 8b83976f4cbec5a7fdaf504e8705df10ff2a164d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 13 Jul 2014 20:27:52 +0200 Subject: [PATCH] Fix arguments of @param, @return etc translations to Python. For the parameter documentation to be really taken as such, in its entirety, by Sphinx, it must be indented relative to the :param: tag. Do this by appending an extra indent after every line of the output and work around the unnecessary indent of the last line by removing the trailing whitespace. This required updating the existing tests and removing the expected but not present any more whitespace from them, but as trailing whitespace in the documentation is at best insignificant (and at worst harmful) anyhow, this is not a big price to pay for simpler translator code. --- .../python/doxygen_basic_translate_runme.py | 2 +- .../python/doxygen_misc_constructs_runme.py | 26 ++--- .../python/doxygen_parsing_runme.py | 4 +- .../doxygen_translate_all_tags_runme.py | 6 +- .../python/doxygen_translate_runme.py | 28 ++--- .../DoxygenTranslator/src/PyDocConverter.cpp | 104 +++++++++++++++++- Source/DoxygenTranslator/src/PyDocConverter.h | 4 + 7 files changed, 135 insertions(+), 39 deletions(-) diff --git a/Examples/test-suite/python/doxygen_basic_translate_runme.py b/Examples/test-suite/python/doxygen_basic_translate_runme.py index 0910ea8ea..3018b72de 100644 --- a/Examples/test-suite/python/doxygen_basic_translate_runme.py +++ b/Examples/test-suite/python/doxygen_basic_translate_runme.py @@ -57,7 +57,7 @@ commentVerifier.check(doxygen_basic_translate.function4.__doc__, ) commentVerifier.check(doxygen_basic_translate.function5.__doc__, """ - This is a post comment. + This is a post comment. """ ) commentVerifier.check(doxygen_basic_translate.function6.__doc__, diff --git a/Examples/test-suite/python/doxygen_misc_constructs_runme.py b/Examples/test-suite/python/doxygen_misc_constructs_runme.py index 742c19b16..ac439ceb4 100644 --- a/Examples/test-suite/python/doxygen_misc_constructs_runme.py +++ b/Examples/test-suite/python/doxygen_misc_constructs_runme.py @@ -23,7 +23,7 @@ commentVerifier.check(doxygen_misc_constructs.getAddress.__doc__, commentVerifier.check(doxygen_misc_constructs.CConnectionConfig.__doc__, r""" - This class contains information for connection to winIDEA. Its methods + This class contains information for connection to winIDEA. Its methods return reference to self, so we can use it like this: CConnectionConfig config = new CConnectionConfig(); @@ -41,7 +41,7 @@ commentVerifier.check(doxygen_misc_constructs.CConnectionConfig.__doc__, commentVerifier.check(doxygen_misc_constructs.waitTime.__doc__, r""" - Determines how long the ``isystem.connect`` should wait for running + Determines how long the ``isystem.connect`` should wait for running instances to respond. Only one of ``lfWaitXXX`` flags from IConnect::ELaunchFlags may be specified. """ @@ -123,8 +123,8 @@ commentVerifier.check(doxygen_misc_constructs.backslashB.__doc__, commentVerifier.check(doxygen_misc_constructs.backslashC.__doc__, r""" - Backslash e at end of *line* froze SWIG - *with* old comment parser. + Backslash e at end of *line* froze SWIG + *with* old comment parser. See also: MyClass::fun(char, float) @@ -146,15 +146,15 @@ commentVerifier.check(doxygen_misc_constructs.cycle.__doc__, Spaces at the start of line should be taken into account: :type id: int :param id: used as prefix in log - statements. The default value is empty string, which is OK if - there is only one app. instance. Example: - - ctrl.setBP("func1"); - - If we set the id to ``main_``, we get: - - main_ctrl.setBP("func1"); - + statements. The default value is empty string, which is OK if + there is only one app. instance. Example: + + ctrl.setBP("func1"); + + If we set the id to ``main_``, we get: + + main_ctrl.setBP("func1"); + :type fileName: string :param fileName: name of the log file diff --git a/Examples/test-suite/python/doxygen_parsing_runme.py b/Examples/test-suite/python/doxygen_parsing_runme.py index 1e5dd7e00..c850e419d 100644 --- a/Examples/test-suite/python/doxygen_parsing_runme.py +++ b/Examples/test-suite/python/doxygen_parsing_runme.py @@ -21,7 +21,7 @@ commentVerifier.check(doxygen_parsing.SomeAnotherClass.classMethod.__doc__, r""" The class method comment. - SomeAnotherClass#classMethodExtended(int, int) a link text + SomeAnotherClass#classMethodExtended(int, int) a link text """) commentVerifier.check(doxygen_parsing.SomeAnotherClass.classMethodExtended.__doc__, r""" @@ -33,7 +33,7 @@ commentVerifier.check(doxygen_parsing.SomeAnotherClass.classMethodExtended.__doc :param b: Parameter b """ ) -commentVerifier.check(doxygen_parsing.SomeAnotherClass.classMethodExtended2.__doc__, +commentVerifier.check(doxygen_parsing.SomeAnotherClass.classMethodExtended2.__doc__, r""" The class method with parameter diff --git a/Examples/test-suite/python/doxygen_translate_all_tags_runme.py b/Examples/test-suite/python/doxygen_translate_all_tags_runme.py index 44b4f536d..c74ba4828 100644 --- a/Examples/test-suite/python/doxygen_translate_all_tags_runme.py +++ b/Examples/test-suite/python/doxygen_translate_all_tags_runme.py @@ -36,7 +36,7 @@ r""" 'citationword' - some test code + some test code """) commentVerifier.check(doxygen_translate_all_tags.func02.__doc__, @@ -95,6 +95,7 @@ r""" :math:`\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}` + .. math:: \sqrt{(x_2-x_1)^2+(y_2-y_1)^2} @@ -116,7 +117,6 @@ r""" - This will only appear in hmtl """) @@ -176,7 +176,7 @@ r""" - someMember Some description follows + someMember Some description follows diff --git a/Examples/test-suite/python/doxygen_translate_runme.py b/Examples/test-suite/python/doxygen_translate_runme.py index 028bf32fd..55c5d8c9a 100644 --- a/Examples/test-suite/python/doxygen_translate_runme.py +++ b/Examples/test-suite/python/doxygen_translate_runme.py @@ -22,7 +22,7 @@ r""" 'citationword' - some test code + some test code Conditional comment: SOMECONDITION Some conditional comment @@ -67,7 +67,7 @@ r""" - someMember Some description follows + someMember Some description follows @@ -171,7 +171,7 @@ r""" *Starts a piece of text displayed in an italic font.* Input tag. - Image: src="slika.png" + Image: src="slika.png" Meta tag. Multicol is ignored by doxygen. @@ -227,16 +227,16 @@ r""" :type byFlags: int :param byFlags: bits marking required items: - - | Size in bits| Items Required | - -------------------------------- - | 1 - 8 | 1 | - | 9 - 16 | 2 | - | 17 - 32 | 4 | - - Almost all combinations of above flags are supported by - ``htmlTable...`` functions. - """) + + | Size in bits| Items Required | + -------------------------------- + | 1 - 8 | 1 | + | 9 - 16 | 2 | + | 17 - 32 | 4 | + + Almost all combinations of above flags are supported by + ``htmlTable...`` functions. + """) commentVerifier.check(doxygen_translate.htmlEntitiesFunction.__doc__, @@ -253,7 +253,7 @@ r""" " - -- - + x - . diff --git a/Source/DoxygenTranslator/src/PyDocConverter.cpp b/Source/DoxygenTranslator/src/PyDocConverter.cpp index afe979e10..d27a677d3 100644 --- a/Source/DoxygenTranslator/src/PyDocConverter.cpp +++ b/Source/DoxygenTranslator/src/PyDocConverter.cpp @@ -24,6 +24,72 @@ std::map 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 " "; } + + // 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. + explicit IndentGuard(string& output, string& indent) : + m_output(output), + m_indent(indent) + { + const size_t 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(); + } + + // 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() + { + 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; + unsigned m_firstLineIndent; + + IndentGuard(const IndentGuard&); + IndentGuard& operator=(const IndentGuard&); +}; + +static void trimWhitespace(string& s) +{ + const size_t lastNonSpace = s.find_last_not_of(' '); + if (lastNonSpace != string::npos) + s.erase(lastNonSpace + 1); +} + /* static */ PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler) @@ -311,13 +377,27 @@ void PyDocConverter::handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string& arg) { + IndentGuard indent(translatedComment, m_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 { - translatedComment += ".. math::\n\n "; + trimWhitespace(translatedComment); + translatedComment += '\n'; + + const string formulaIndent = indent.getFirstLineIndent(); + translatedComment += formulaIndent; + translatedComment += ".. math::\n"; + + formulaNL = '\n'; + formulaNL += formulaIndent; + formulaNL += m_indent; + translatedComment += formulaNL; } std::string formula; @@ -330,10 +410,9 @@ void PyDocConverter::handleMath(DoxygenEntity &tag, 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. + // New lines must be suppressed in inline maths and indented in the block ones. if (!inlineFormula) - translatedComment += "\n "; + translatedComment += formulaNL; } else { // Just copy everything else. translatedComment += formula[n]; @@ -344,7 +423,7 @@ void PyDocConverter::handleMath(DoxygenEntity &tag, if (inlineFormula) { translatedComment += "`"; } else { - translatedComment += "\n\n"; + translatedComment += '\n'; } } @@ -427,16 +506,21 @@ void PyDocConverter::handleTagParam(DoxygenEntity& tag, 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); - if (!paramType.empty()) + if (!paramType.empty()) { translatedComment += ":type " + paramName + ": " + paramType + "\n"; + translatedComment += indent.getFirstLineIndent(); + } translatedComment += ":param " + paramName + ":"; + handleParagraph(tag, translatedComment); } @@ -445,6 +529,8 @@ void PyDocConverter::handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &) { + IndentGuard indent(translatedComment, m_indent); + translatedComment += ":return: "; handleParagraph(tag, translatedComment); } @@ -454,6 +540,8 @@ void PyDocConverter::handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &) { + IndentGuard indent(translatedComment, m_indent); + translatedComment += ":raises: "; handleParagraph(tag, translatedComment); } @@ -618,7 +706,11 @@ 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) diff --git a/Source/DoxygenTranslator/src/PyDocConverter.h b/Source/DoxygenTranslator/src/PyDocConverter.h index 9257e14a8..dae0ec62c 100644 --- a/Source/DoxygenTranslator/src/PyDocConverter.h +++ b/Source/DoxygenTranslator/src/PyDocConverter.h @@ -174,6 +174,10 @@ 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 > TagHandlersMap; static TagHandlersMap tagHandlers;