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;