Ensure empty line before code and math blocks in doxygen pydoc

Sphinx requires an empty line before code and math blocks, whereas
doxygen does not.  This update ensures that a blank line is included
before generated code and math blocks in the pydoc output.  This is
done by post-processing the docstring line by line to check whether
any newlines need to be added.  This way, if the original doxygen
source already includes an empty line before a block, an additional
unnecessary empty line is not added.

Updating the expected test output for doxygen_basic_translate, which
now adds the necessary empty line before the code block.  Adding
further test cases to doxygen_translate_all_tags to explicitly verify
that a newline is added in the pydoc output before both code and math
blocks that appear within a paragraph.

Additionally, empty lines previously appearing at the beginning of the
generated docstrings are now removed.  This does not alter the
behavior of the tests.
This commit is contained in:
John McFarland 2019-06-04 16:55:54 -05:00
commit daad5d664d
6 changed files with 75 additions and 6 deletions

View file

@ -140,16 +140,50 @@ static void trimWhitespace(string &s) {
// Erase the first character in the string if it is a newline
static void eraseLeadingNewLine(string &s) {
if ((! s.empty()) && s[0] == '\n')
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')
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)) {
// Must separate code or math blocks from the previous line
result += '\n';
}
lastLineWasNonBlank = true;
}
}
return result;
}
/* static */
PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler) {
return make_pair(handler, std::string());
@ -863,6 +897,9 @@ String *PyDocConverter::makeDocumentation(Node *n) {
// 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;