/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmDocumentationFormatter.h" #include "cmDocumentationEntry.h" #include "cmDocumentationSection.h" #include #include #include #include cmDocumentationFormatter::cmDocumentationFormatter() : TextWidth(77) , TextIndent("") { } cmDocumentationFormatter::~cmDocumentationFormatter() { } void cmDocumentationFormatter::PrintFormatted(std::ostream& os, const char* text) { if (!text) { return; } const char* ptr = text; while (*ptr) { // Any ptrs starting in a space are treated as preformatted text. std::string preformatted; while (*ptr == ' ') { for (char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr) { preformatted.append(1, ch); } if (*ptr) { ++ptr; preformatted.append(1, '\n'); } } if (!preformatted.empty()) { this->PrintPreformatted(os, preformatted.c_str()); } // Other ptrs are treated as paragraphs. std::string paragraph; for (char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr) { paragraph.append(1, ch); } if (*ptr) { ++ptr; paragraph.append(1, '\n'); } if (!paragraph.empty()) { this->PrintParagraph(os, paragraph.c_str()); } } } void cmDocumentationFormatter::PrintPreformatted(std::ostream& os, const char* text) { bool newline = true; for (const char* ptr = text; *ptr; ++ptr) { if (newline && *ptr != '\n') { os << this->TextIndent; newline = false; } os << *ptr; if (*ptr == '\n') { newline = true; } } os << "\n"; } void cmDocumentationFormatter::PrintParagraph(std::ostream& os, const char* text) { os << this->TextIndent; this->PrintColumn(os, text); os << "\n"; } void cmDocumentationFormatter::SetIndent(const char* indent) { this->TextIndent = indent; } void cmDocumentationFormatter::PrintColumn(std::ostream& os, const char* text) { // Print text arranged in an indented column of fixed width. const char* l = text; long column = 0; bool newSentence = false; bool firstLine = true; int width = this->TextWidth - static_cast(strlen(this->TextIndent)); // Loop until the end of the text. while (*l) { // Parse the next word. const char* r = l; while (*r && (*r != '\n') && (*r != ' ')) { ++r; } // Does it fit on this line? if (r - l < (width - column - (newSentence ? 1 : 0))) { // Word fits on this line. if (r > l) { if (column) { // Not first word on line. Separate from the previous word // by a space, or two if this is a new sentence. if (newSentence) { os << " "; column += 2; } else { os << " "; column += 1; } } else { // First word on line. Print indentation unless this is the // first line. os << (firstLine ? "" : this->TextIndent); } // Print the word. os.write(l, static_cast(r - l)); newSentence = (*(r - 1) == '.'); } if (*r == '\n') { // Text provided a newline. Start a new line. os << "\n"; ++r; column = 0; firstLine = false; } else { // No provided newline. Continue this line. column += static_cast(r - l); } } else { // Word does not fit on this line. Start a new line. os << "\n"; firstLine = false; if (r > l) { os << this->TextIndent; os.write(l, static_cast(r - l)); column = static_cast(r - l); newSentence = (*(r - 1) == '.'); } else { column = 0; } } // Move to beginning of next word. Skip over whitespace. l = r; while (*l == ' ') { ++l; } } } void cmDocumentationFormatter::PrintSection( std::ostream& os, cmDocumentationSection const& section) { os << section.GetName() << "\n"; const std::vector& entries = section.GetEntries(); for (cmDocumentationEntry const& entry : entries) { if (!entry.Name.empty()) { os << " " << entry.Name; this->TextIndent = " "; int align = static_cast(strlen(this->TextIndent)) - 4; for (int i = static_cast(entry.Name.size()); i < align; ++i) { os << " "; } if (entry.Name.size() > strlen(this->TextIndent) - 4) { os << "\n"; os.write(this->TextIndent, strlen(this->TextIndent) - 2); } os << "= "; this->PrintColumn(os, entry.Brief.c_str()); os << "\n"; } else { os << "\n"; this->TextIndent = ""; this->PrintFormatted(os, entry.Brief.c_str()); } } os << "\n"; }