summaryrefslogtreecommitdiffstats
path: root/Source/cmDocumentationFormatter.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmDocumentationFormatter.cxx')
-rw-r--r--Source/cmDocumentationFormatter.cxx234
1 files changed, 132 insertions, 102 deletions
diff --git a/Source/cmDocumentationFormatter.cxx b/Source/cmDocumentationFormatter.cxx
index 732637e..70ba1fc 100644
--- a/Source/cmDocumentationFormatter.cxx
+++ b/Source/cmDocumentationFormatter.cxx
@@ -2,7 +2,8 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmDocumentationFormatter.h"
-#include <cstring>
+#include <algorithm>
+#include <cassert>
#include <iomanip>
#include <ostream>
#include <string>
@@ -11,178 +12,207 @@
#include "cmDocumentationEntry.h"
#include "cmDocumentationSection.h"
-cmDocumentationFormatter::cmDocumentationFormatter() = default;
-
-cmDocumentationFormatter::~cmDocumentationFormatter() = default;
+namespace {
+const char* skipSpaces(const char* ptr)
+{
+ assert(ptr);
+ for (; *ptr == ' '; ++ptr) {
+ ;
+ }
+ return ptr;
+}
+const char* skipToSpace(const char* ptr)
+{
+ assert(ptr);
+ for (; *ptr && (*ptr != '\n') && (*ptr != ' '); ++ptr) {
+ ;
+ }
+ return ptr;
+}
+}
void cmDocumentationFormatter::PrintFormatted(std::ostream& os,
- const char* text)
+ std::string const& text) const
{
- if (!text) {
+ if (text.empty()) {
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());
+
+ struct Buffer
+ {
+ // clang-format off
+ using PrinterFn = void (cmDocumentationFormatter::*)(
+ std::ostream&, std::string const&
+ ) const;
+ // clang-format on
+ std::string collected;
+ const PrinterFn printer;
+ };
+ // const auto NORMAL_IDX = 0u;
+ const auto PREFORMATTED_IDX = 1u;
+ const auto HANDLERS_SIZE = 2u;
+ Buffer buffers[HANDLERS_SIZE] = {
+ { {}, &cmDocumentationFormatter::PrintParagraph },
+ { {}, &cmDocumentationFormatter::PrintPreformatted }
+ };
+
+ const auto padding = std::string(this->TextIndent, ' ');
+
+ for (std::size_t pos = 0u, eol = 0u; pos < text.size(); pos = eol) {
+ const auto current_idx = std::size_t(text[pos] == ' ');
+ // size_t(!bool(current_idx))
+ const auto other_idx = current_idx ^ 1u;
+
+ // Flush the other buffer if anything has been collected
+ if (!buffers[other_idx].collected.empty()) {
+ // NOTE Whatever the other index is, the current buffered
+ // string expected to be empty.
+ assert(buffers[current_idx].collected.empty());
+
+ (this->*buffers[other_idx].printer)(os, buffers[other_idx].collected);
+ buffers[other_idx].collected.clear();
}
- // Other ptrs are treated as paragraphs.
- std::string paragraph;
- for (char ch = *ptr; ch && ch != '\n'; ++ptr, ch = *ptr) {
- paragraph.append(1, ch);
+ // ATTENTION The previous implementation had called `PrintParagraph()`
+ // **for every processed (char by char) input line**.
+ // The method unconditionally append the `\n' character after the
+ // printed text. To keep the backward-compatible behavior it's needed to
+ // add the '\n' character to the previously collected line...
+ if (!buffers[current_idx].collected.empty() &&
+ current_idx != PREFORMATTED_IDX) {
+ buffers[current_idx].collected += '\n';
}
- if (*ptr) {
- ++ptr;
- paragraph.append(1, '\n');
+
+ // Lookup EOL
+ eol = text.find('\n', pos);
+ if (current_idx == PREFORMATTED_IDX) {
+ buffers[current_idx].collected.append(padding);
}
- if (!paragraph.empty()) {
- this->PrintParagraph(os, paragraph.c_str());
+ buffers[current_idx].collected.append(
+ text, pos, eol == std::string::npos ? eol : ++eol - pos);
+ }
+
+ for (auto& buf : buffers) {
+ if (!buf.collected.empty()) {
+ (this->*buf.printer)(os, buf.collected);
}
}
}
void cmDocumentationFormatter::PrintPreformatted(std::ostream& os,
- const char* text)
+ std::string const& text) const
{
- 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";
+ os << text << '\n';
}
void cmDocumentationFormatter::PrintParagraph(std::ostream& os,
- const char* text)
+ std::string const& text) const
{
- os << this->TextIndent;
+ if (this->TextIndent) {
+ os << std::string(this->TextIndent, ' ');
+ }
this->PrintColumn(os, text);
- os << "\n";
-}
-
-void cmDocumentationFormatter::SetIndent(const char* indent)
-{
- this->TextIndent = indent;
+ os << '\n';
}
-void cmDocumentationFormatter::PrintColumn(std::ostream& os, const char* text)
+void cmDocumentationFormatter::PrintColumn(std::ostream& os,
+ std::string const& text) const
{
// 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;
- }
+ assert(this->TextIndent < this->TextWidth);
+ const std::ptrdiff_t width = this->TextWidth - this->TextIndent;
+ std::ptrdiff_t column = 0;
+ // Loop until the end of the text.
+ for (const char *l = text.c_str(), *r = skipToSpace(text.c_str()); *l;
+ l = skipSpaces(r), r = skipToSpace(l)) {
// Does it fit on this line?
- if (r - l < (width - column - (newSentence ? 1 : 0))) {
+ if (r - l < width - column - std::ptrdiff_t(newSentence)) {
// 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 {
+ os << &(" "[std::size_t(!newSentence)]);
+ column += 1u + std::ptrdiff_t(newSentence);
+ } else if (!firstLine && this->TextIndent) {
// First word on line. Print indentation unless this is the
// first line.
- os << (firstLine ? "" : this->TextIndent);
+ os << std::string(this->TextIndent, ' ');
}
// Print the word.
- os.write(l, static_cast<long>(r - l));
+ os.write(l, r - l);
newSentence = (*(r - 1) == '.');
}
if (*r == '\n') {
// Text provided a newline. Start a new line.
- os << "\n";
+ os << '\n';
++r;
column = 0;
firstLine = false;
} else {
// No provided newline. Continue this line.
- column += static_cast<long>(r - l);
+ column += r - l;
}
} else {
// Word does not fit on this line. Start a new line.
- os << "\n";
+ os << '\n';
firstLine = false;
if (r > l) {
- os << this->TextIndent;
- os.write(l, static_cast<long>(r - l));
- column = static_cast<long>(r - l);
+ os << std::string(this->TextIndent, ' ');
+ os.write(l, r - l);
+ column = 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::size_t PREFIX_SIZE =
+ sizeof(cmDocumentationEntry::CustomNamePrefix) + 1u;
+ // length of the "= " literal (see below)
+ const std::size_t SUFFIX_SIZE = 2u;
+ // legacy magic number ;-)
+ const std::size_t NAME_SIZE = 29u;
+
+ const std::size_t PADDING_SIZE = PREFIX_SIZE + SUFFIX_SIZE;
+ const std::size_t TITLE_SIZE = NAME_SIZE + PADDING_SIZE;
+
+ const auto savedIndent = this->TextIndent;
- const std::vector<cmDocumentationEntry>& entries = section.GetEntries();
- for (cmDocumentationEntry const& entry : entries) {
+ os << section.GetName() << '\n';
+
+ for (cmDocumentationEntry const& entry : section.GetEntries()) {
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);
+ this->TextIndent = TITLE_SIZE;
+ os << std::setw(PREFIX_SIZE) << std::left << entry.CustomNamePrefix
+ << std::setw(int(std::max(NAME_SIZE, entry.Name.size())))
+ << entry.Name;
+ if (entry.Name.size() > NAME_SIZE) {
+ os << '\n' << std::setw(int(this->TextIndent - PREFIX_SIZE)) << ' ';
}
os << "= ";
- this->PrintColumn(os, entry.Brief.c_str());
- os << "\n";
+ this->PrintColumn(os, entry.Brief);
+ os << '\n';
} else {
- os << "\n";
- this->TextIndent = "";
- this->PrintFormatted(os, entry.Brief.c_str());
+ os << '\n';
+ this->TextIndent = 0u;
+ this->PrintFormatted(os, entry.Brief);
}
}
- os << "\n";
+
+ os << '\n';
+
+ this->TextIndent = savedIndent;
}