summary refs log tree commit diff
path: root/src/Utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Utils.cpp')
-rw-r--r--src/Utils.cpp90
1 files changed, 86 insertions, 4 deletions
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 8a3b9e4c..32d435f0 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -17,6 +17,7 @@
 #include <QTextDocument>
 #include <QXmlStreamReader>
 
+#include <QTextBoundaryFinder>
 #include <cmath>
 #include <variant>
 
@@ -468,11 +469,92 @@ utils::escapeBlacklistedHtml(const QString &rawStr)
 }
 
 QString
-utils::markdownToHtml(const QString &text)
-{
-        const auto str      = text.toUtf8();
-        const char *tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_UNSAFE);
+utils::markdownToHtml(const QString &text, bool rainbowify)
+{
+        const auto str = text.toUtf8();
+        cmark_node *const node =
+          cmark_parse_document(str.constData(), str.size(), CMARK_OPT_UNSAFE);
+
+        if (rainbowify) {
+                // create iterator over node
+                cmark_iter *iter = cmark_iter_new(node);
+
+                cmark_event_type ev_type;
+
+                // First loop to get total text length
+                int textLen = 0;
+                while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
+                        cmark_node *cur = cmark_iter_get_node(iter);
+                        // only text nodes (no code or semilar)
+                        if (cmark_node_get_type(cur) != CMARK_NODE_TEXT)
+                                continue;
+                        // count up by length of current node's text
+                        QTextBoundaryFinder tbf(QTextBoundaryFinder::BoundaryType::Grapheme,
+                                                QString(cmark_node_get_literal(cur)));
+                        while (tbf.toNextBoundary() != -1)
+                                textLen++;
+                }
+
+                // create new iter to start over
+                cmark_iter_free(iter);
+                iter = cmark_iter_new(node);
+
+                // Second loop to rainbowify
+                int charIdx = 0;
+                while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
+                        cmark_node *cur = cmark_iter_get_node(iter);
+                        // only text nodes (no code or semilar)
+                        if (cmark_node_get_type(cur) != CMARK_NODE_TEXT)
+                                continue;
+
+                        // get text in current node
+                        QString nodeText(cmark_node_get_literal(cur));
+                        // create buffer to append rainbow text to
+                        QString buf;
+                        int boundaryStart = 0;
+                        int boundaryEnd   = 0;
+                        // use QTextBoundaryFinder to iterate ofer graphemes
+                        QTextBoundaryFinder tbf(QTextBoundaryFinder::BoundaryType::Grapheme,
+                                                nodeText);
+                        while ((boundaryEnd = tbf.toNextBoundary()) != -1) {
+                                // Split text to get current char
+                                auto curChar =
+                                  nodeText.midRef(boundaryStart, boundaryEnd - boundaryStart);
+                                boundaryStart = boundaryEnd;
+                                // Don't rainbowify whitespaces
+                                if (curChar.trimmed().isEmpty()) {
+                                        buf.append(curChar.toString());
+                                        continue;
+                                }
+
+                                // get correct color for char index
+                                auto color = QColor::fromHsvF(1.0 / textLen * charIdx, 1.0, 1.0);
+                                // format color for HTML
+                                auto colorString = color.name(QColor::NameFormat::HexRgb);
+                                // create HTML element for current char
+                                auto curCharColored = QString("<font color=\"%0\">%1</font>")
+                                                        .arg(colorString)
+                                                        .arg(curChar);
+                                // append colored HTML element to buffer
+                                buf.append(curCharColored);
+
+                                charIdx++;
+                        }
+
+                        // create HTML_INLINE node to prevent HTML from being escaped
+                        auto htmlNode = cmark_node_new(CMARK_NODE_HTML_INLINE);
+                        // set content of HTML node to buffer contents
+                        cmark_node_set_literal(htmlNode, buf.toUtf8().data());
+                        // replace current node with HTML node
+                        cmark_node_replace(cur, htmlNode);
+                        // free memory of old node
+                        cmark_node_free(cur);
+                }
+
+                cmark_iter_free(iter);
+        }
 
+        const char *tmp_buf = cmark_render_html(node, CMARK_OPT_UNSAFE);
         // Copy the null terminated output buffer.
         std::string html(tmp_buf);