| /* 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 <cstring> |
| #include <iomanip> |
| #include <ostream> |
| #include <string> |
| #include <vector> |
| |
| #include "cmDocumentationEntry.h" |
| #include "cmDocumentationSection.h" |
| |
| cmDocumentationFormatter::cmDocumentationFormatter() = default; |
| |
| cmDocumentationFormatter::~cmDocumentationFormatter() = default; |
| |
| 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<int>(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<long>(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<long>(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<long>(r - l)); |
| column = static_cast<long>(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<cmDocumentationEntry>& entries = section.GetEntries(); |
| for (cmDocumentationEntry const& entry : entries) { |
| if (!entry.Name.empty()) { |
| os << std::setw(2) << std::left << entry.CustomNamePrefix << entry.Name; |
| this->TextIndent = " "; |
| int align = static_cast<int>(strlen(this->TextIndent)) - 4; |
| for (int i = static_cast<int>(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"; |
| } |