summaryrefslogtreecommitdiffstats
path: root/src/qdoc/qdoc/src/qdoc/text.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qdoc/qdoc/src/qdoc/text.cpp')
-rw-r--r--src/qdoc/qdoc/src/qdoc/text.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/qdoc/qdoc/src/qdoc/text.cpp b/src/qdoc/qdoc/src/qdoc/text.cpp
new file mode 100644
index 000000000..d3401ce68
--- /dev/null
+++ b/src/qdoc/qdoc/src/qdoc/text.cpp
@@ -0,0 +1,341 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "text.h"
+
+#include <QtCore/qregularexpression.h>
+
+#include <cstdio>
+
+QT_BEGIN_NAMESPACE
+
+Text::Text() : m_first(nullptr), m_last(nullptr) { }
+
+Text::Text(const QString &str) : m_first(nullptr), m_last(nullptr)
+{
+ operator<<(str);
+}
+
+Text::Text(const Text &text) : m_first(nullptr), m_last(nullptr)
+{
+ operator=(text);
+}
+
+Text::~Text()
+{
+ clear();
+}
+
+Text &Text::operator=(const Text &text)
+{
+ if (this != &text) {
+ clear();
+ operator<<(text);
+ }
+ return *this;
+}
+
+Text &Text::operator<<(Atom::AtomType atomType)
+{
+ return operator<<(Atom(atomType));
+}
+
+Text &Text::operator<<(const QString &string)
+{
+ return string.isEmpty() ? *this : operator<<(Atom(Atom::String, string));
+}
+
+Text &Text::operator<<(const Atom &atom)
+{
+ if (atom.count() < 2) {
+ if (m_first == nullptr) {
+ m_first = new Atom(atom.type(), atom.string());
+ m_last = m_first;
+ } else
+ m_last = new Atom(m_last, atom.type(), atom.string());
+ } else {
+ if (m_first == nullptr) {
+ m_first = new Atom(atom.type(), atom.string(), atom.string(1));
+ m_last = m_first;
+ } else
+ m_last = new Atom(m_last, atom.type(), atom.string(), atom.string(1));
+ }
+ return *this;
+}
+
+/*!
+ Special output operator for LinkAtom. It makes a copy of
+ the LinkAtom \a atom and connects the cop;y to the list
+ in this Text.
+ */
+Text &Text::operator<<(const LinkAtom &atom)
+{
+ if (m_first == nullptr) {
+ m_first = new LinkAtom(atom);
+ m_last = m_first;
+ } else
+ m_last = new LinkAtom(m_last, atom);
+ return *this;
+}
+
+Text &Text::operator<<(const Text &text)
+{
+ const Atom *atom = text.firstAtom();
+ while (atom != nullptr) {
+ operator<<(*atom);
+ atom = atom->next();
+ }
+ return *this;
+}
+
+void Text::stripFirstAtom()
+{
+ if (m_first != nullptr) {
+ if (m_first == m_last)
+ m_last = nullptr;
+ Atom *oldFirst = m_first;
+ m_first = m_first->next();
+ delete oldFirst;
+ }
+}
+
+void Text::stripLastAtom()
+{
+ if (m_last != nullptr) {
+ Atom *oldLast = m_last;
+ if (m_first == m_last) {
+ m_first = nullptr;
+ m_last = nullptr;
+ } else {
+ m_last = m_first;
+ while (m_last->next() != oldLast)
+ m_last = m_last->next();
+ m_last->setNext(nullptr);
+ }
+ delete oldLast;
+ }
+}
+
+/*!
+ This function traverses the atom list of the Text object,
+ extracting all the string parts. It concatenates them to
+ a result string and returns it.
+ */
+QString Text::toString() const
+{
+ QString str;
+ const Atom *atom = firstAtom();
+ while (atom != nullptr) {
+ if (atom->type() == Atom::String || atom->type() == Atom::AutoLink
+ || atom->type() == Atom::C)
+ str += atom->string();
+ atom = atom->next();
+ }
+ return str;
+}
+
+/*!
+ Returns true if this Text contains the substring \a str.
+ */
+bool Text::contains(const QString &str) const
+{
+ const Atom *atom = firstAtom();
+ while (atom != nullptr) {
+ if (atom->type() == Atom::String || atom->type() == Atom::AutoLink
+ || atom->type() == Atom::C)
+ if (atom->string().contains(str, Qt::CaseInsensitive))
+ return true;
+ atom = atom->next();
+ }
+ return false;
+}
+
+Text Text::subText(Atom::AtomType left, Atom::AtomType right, const Atom *from,
+ bool inclusive) const
+{
+ const Atom *begin = from ? from : firstAtom();
+ const Atom *end;
+
+ while (begin != nullptr && begin->type() != left)
+ begin = begin->next();
+ if (begin != nullptr) {
+ if (!inclusive)
+ begin = begin->next();
+ }
+
+ end = begin;
+ while (end != nullptr && end->type() != right)
+ end = end->next();
+ if (end == nullptr)
+ begin = nullptr;
+ else if (inclusive)
+ end = end->next();
+ return subText(begin, end);
+}
+
+Text Text::sectionHeading(const Atom *sectionLeft)
+{
+ if (sectionLeft != nullptr) {
+ const Atom *begin = sectionLeft;
+ while (begin != nullptr && begin->type() != Atom::SectionHeadingLeft)
+ begin = begin->next();
+ if (begin != nullptr)
+ begin = begin->next();
+
+ const Atom *end = begin;
+ while (end != nullptr && end->type() != Atom::SectionHeadingRight)
+ end = end->next();
+
+ if (end != nullptr)
+ return subText(begin, end);
+ }
+ return Text();
+}
+
+/*!
+ Prints a human-readable version of the contained atoms to stderr.
+
+ The output is formatted as a linear list of atoms, with each atom
+ being on its own line.
+
+ Each atom is represented by its type and its stringified-contents,
+ if any, with a space between the two.
+
+ Indentation is used to emphasize the possible block-level
+ relationship between consecutive atoms, increasing after a
+ "Left" atom and decreasing just before a "Right" atom.
+
+ For example, if this `Text` represented the block-comment
+ containing the text:
+
+ \c {\l {somelink} {This is a link}}
+
+ Then the human-readable output would look like the following:
+
+ \badcode
+ ParaLeft
+ Link "somelink"
+ FormattingLeft "link"
+ String "This is a link"
+ FormattingRight "link"
+ String
+ ParaRight
+ \endcode
+ */
+void Text::dump() const
+{
+ constexpr int minimum_indentation_level { 1 };
+ int indentation_level { minimum_indentation_level };
+ int indentation_width { 4 };
+
+ const Atom *atom = firstAtom();
+ while (atom != nullptr) {
+ QString str = atom->string();
+ str.replace("\\", "\\\\");
+ str.replace("\"", "\\\"");
+ str.replace("\n", "\\n");
+ static const QRegularExpression re(R"([^ -~])");
+ str.replace(re, "?");
+ if (!str.isEmpty())
+ str = " \"" + str + QLatin1Char('"');
+
+ QString atom_type = atom->typeString();
+ if (atom_type.contains("Right"))
+ indentation_level = std::max(minimum_indentation_level, indentation_level - 1);
+
+ fprintf(stderr, "%s%s%s\n",
+ QString(indentation_level * indentation_width, ' ').toLatin1().data(),
+ atom_type.toLatin1().data(), str.toLatin1().data());
+
+ if (atom_type.contains("Left"))
+ indentation_level += 1;
+
+ atom = atom->next();
+ }
+}
+
+Text Text::subText(const Atom *begin, const Atom *end)
+{
+ Text text;
+ if (begin != nullptr) {
+ while (begin != end) {
+ text << *begin;
+ begin = begin->next();
+ }
+ }
+ return text;
+}
+
+void Text::clear()
+{
+ while (m_first != nullptr) {
+ Atom *atom = m_first;
+ m_first = m_first->next();
+ delete atom;
+ }
+ m_first = nullptr;
+ m_last = nullptr;
+}
+
+int Text::compare(const Text &text1, const Text &text2)
+{
+ if (text1.isEmpty())
+ return text2.isEmpty() ? 0 : -1;
+ if (text2.isEmpty())
+ return 1;
+
+ const Atom *atom1 = text1.firstAtom();
+ const Atom *atom2 = text2.firstAtom();
+
+ for (;;) {
+ if (atom1->type() != atom2->type())
+ return (int)atom1->type() - (int)atom2->type();
+ int cmp = QString::compare(atom1->string(), atom2->string());
+ if (cmp != 0)
+ return cmp;
+
+ if (atom1 == text1.lastAtom())
+ return atom2 == text2.lastAtom() ? 0 : -1;
+ if (atom2 == text2.lastAtom())
+ return 1;
+ atom1 = atom1->next();
+ atom2 = atom2->next();
+ }
+}
+
+/*!
+ \internal
+
+ \brief Splits the current Text from \a start to end into a new Text object.
+
+ Returns a new Text from the first Atom in this Text of atom type \a start.
+ */
+Text Text::splitAtFirst(Atom::AtomType start) {
+ if (m_first == nullptr)
+ return {};
+
+ Atom *previous = nullptr;
+ Atom *current = m_first;
+
+ while (current != nullptr) {
+ if (current->type() == start)
+ break;
+ previous = current;
+ current = current->next();
+ }
+
+ if (!current)
+ return {};
+
+ Text splitText = Text(current, m_last);
+
+ // Reset this Text's first and last atom pointers based on
+ // whether all or part of the content was extracted.
+ m_first = previous ? m_first : nullptr;
+ if (m_last = previous; m_last)
+ m_last->setNext(nullptr);
+
+ return splitText;
+}
+
+QT_END_NAMESPACE