diff options
author | Nicolas Werner <nicolas.werner@hotmail.de> | 2023-01-31 18:22:12 +0100 |
---|---|---|
committer | Nicolas Werner <nicolas.werner@hotmail.de> | 2023-01-31 18:22:12 +0100 |
commit | 0c3d46795b589d9dfb2e6caa7f94db77fd5371bd (patch) | |
tree | 4d3bdc9cee84be37f51465e49ec38a9055452dd0 | |
parent | Uncoditionally request keyframes (diff) | |
download | nheko-0c3d46795b589d9dfb2e6caa7f94db77fd5371bd.tar.xz |
Make single newlines cause a <br> by default
This should match what people expect from a chat application much better. The biggest reason not to do this, is because some people might paste markdown documents. For those people there is now a /cmark command, which disables most of our extensions to cmark, including the newline behaviour. There is a long discussion on the Fediverse and on Github linked below. Mastodon https://fosstodon.org/@deepbluev7/109771668066978726 fixes #757
-rw-r--r-- | src/CommandCompleter.cpp | 7 | ||||
-rw-r--r-- | src/CommandCompleter.h | 1 | ||||
-rw-r--r-- | src/UserDirectoryModel.cpp | 11 | ||||
-rw-r--r-- | src/UserDirectoryModel.h | 8 | ||||
-rw-r--r-- | src/Utils.cpp | 23 | ||||
-rw-r--r-- | src/Utils.h | 2 | ||||
-rw-r--r-- | src/timeline/InputBar.cpp | 10 | ||||
-rw-r--r-- | src/timeline/InputBar.h | 1 |
8 files changed, 48 insertions, 15 deletions
diff --git a/src/CommandCompleter.cpp b/src/CommandCompleter.cpp index 4cc61291..51eef4f5 100644 --- a/src/CommandCompleter.cpp +++ b/src/CommandCompleter.cpp @@ -73,6 +73,8 @@ CommandCompleter::data(const QModelIndex &index, int role) const return QString("/rotate-megolm-session"); case Md: return QString("/md "); + case Cmark: + return QString("/cmark "); case Plain: return QString("/plain "); case Rainbow: @@ -140,6 +142,8 @@ CommandCompleter::data(const QModelIndex &index, int role) const return tr("/rotate-megolm-session"); case Md: return tr("/md [message]"); + case Cmark: + return tr("/cmark [message]"); case Plain: return tr("/plain [message]"); case Rainbow: @@ -206,6 +210,9 @@ CommandCompleter::data(const QModelIndex &index, int role) const return tr("Rotate the current symmetric encryption key."); case Md: return tr("Send a markdown formatted message (ignoring the global setting)."); + case Cmark: + return tr( + "Send a commonmark formatted message disabling most extensions compared to /md."); case Plain: return tr("Send an unformatted message (ignoring the global setting)."); case Rainbow: diff --git a/src/CommandCompleter.h b/src/CommandCompleter.h index 24242209..a3339c7a 100644 --- a/src/CommandCompleter.h +++ b/src/CommandCompleter.h @@ -40,6 +40,7 @@ public: ResetState, RotateMegolmSession, Md, + Cmark, Plain, Rainbow, RainbowMe, diff --git a/src/UserDirectoryModel.cpp b/src/UserDirectoryModel.cpp index 2c44df40..37753ad8 100644 --- a/src/UserDirectoryModel.cpp +++ b/src/UserDirectoryModel.cpp @@ -6,11 +6,13 @@ #include "UserDirectoryModel.h" +#include <QSharedPointer> + +#include <mtx/responses/users.hpp> + #include "Cache.h" #include "Logging.h" -#include <QSharedPointer> #include "MatrixClient.h" -#include "mtx/responses/users.hpp" UserDirectoryModel::UserDirectoryModel(QObject *parent) : QAbstractListModel{parent} @@ -49,7 +51,7 @@ UserDirectoryModel::fetchMore(const QModelIndex &) nhlog::net()->debug("Fetching users from mtxclient..."); std::string searchTerm = userSearchString_; - searchingUsers_ = true; + searchingUsers_ = true; emit searchingUsersChanged(); auto job = QSharedPointer<FetchUsersFromDirectoryJob>::create(); connect(job.data(), @@ -88,7 +90,8 @@ UserDirectoryModel::data(const QModelIndex &index, int role) const } void -UserDirectoryModel::displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm) +UserDirectoryModel::displaySearchResults(std::vector<mtx::responses::User> results, + const std::string &searchTerm) { if (searchTerm != this->userSearchString_) return; diff --git a/src/UserDirectoryModel.h b/src/UserDirectoryModel.h index 87f8163c..cdceed46 100644 --- a/src/UserDirectoryModel.h +++ b/src/UserDirectoryModel.h @@ -18,11 +18,12 @@ class FetchUsersFromDirectoryJob final : public QObject Q_OBJECT public: explicit FetchUsersFromDirectoryJob(QObject *p = nullptr) - : QObject(p) + : QObject(p) { } signals: - void fetchedSearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm); + void + fetchedSearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm); }; class UserDirectoryModel : public QAbstractListModel { @@ -65,5 +66,6 @@ public slots: bool searchingUsers() const { return searchingUsers_; } private slots: - void displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm); + void + displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm); }; diff --git a/src/Utils.cpp b/src/Utils.cpp index 218edb62..649e9124 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -901,19 +901,24 @@ process_strikethrough(cmark_node *node) cmark_iter_free(iter); } QString -utils::markdownToHtml(const QString &text, bool rainbowify_) +utils::markdownToHtml(const QString &text, bool rainbowify_, bool noExtensions) { const auto str = text.toUtf8(); cmark_node *const node = cmark_parse_document(str.constData(), str.size(), CMARK_OPT_UNSAFE); - process_strikethrough(node); - process_spoilers(node); + if (!noExtensions) { + process_strikethrough(node); + process_spoilers(node); - if (rainbowify_) { - rainbowify(node); + if (rainbowify_) { + rainbowify(node); + } } - const char *tmp_buf = cmark_render_html(node, CMARK_OPT_UNSAFE); + const char *tmp_buf = cmark_render_html( + node, + // by default make single linebreaks <br> tags + noExtensions ? CMARK_OPT_UNSAFE : (CMARK_OPT_UNSAFE | CMARK_OPT_HARDBREAKS)); // Copy the null terminated output buffer. std::string html(tmp_buf); @@ -921,7 +926,11 @@ utils::markdownToHtml(const QString &text, bool rainbowify_) free((char *)tmp_buf); cmark_node_free(node); - auto result = linkifyMessage(escapeBlacklistedHtml(QString::fromStdString(html))).trimmed(); + auto result = escapeBlacklistedHtml(QString::fromStdString(html)).trimmed(); + + if (!noExtensions) { + result = linkifyMessage(std::move(result)).trimmed(); + } if (result.count(QStringLiteral("<p>")) == 1 && result.startsWith(QLatin1String("<p>")) && result.endsWith(QLatin1String("</p>"))) { diff --git a/src/Utils.h b/src/Utils.h index e6d66f47..18a1cb78 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -276,7 +276,7 @@ linkifyMessage(const QString &body); //! Convert the input markdown text to html. QString -markdownToHtml(const QString &text, bool rainbowify = false); +markdownToHtml(const QString &text, bool rainbowify = false, bool noExtensions = false); //! Escape every html tag, that was not whitelisted QString diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index b6355418..94944337 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -465,6 +465,14 @@ InputBar::message(const QString &msg, MarkdownOverride useMarkdown, bool rainbow text.formatted_body = ""; else text.format = "org.matrix.custom.html"; + } else if (useMarkdown == MarkdownOverride::CMARK) { + // disable all markdown extensions + text.formatted_body = utils::markdownToHtml(msg, rainbowify, true).toStdString(); + // keep everything as it was + text.body = msg.trimmed().toStdString(); + + // always send formatted + text.format = "org.matrix.custom.html"; } text.relations = generateRelations(); @@ -802,6 +810,8 @@ InputBar::command(const QString &command, QString args) cache::dropOutboundMegolmSession(room->roomId().toStdString()); } else if (command == QLatin1String("md")) { message(args, MarkdownOverride::ON); + } else if (command == QLatin1String("cmark")) { + message(args, MarkdownOverride::CMARK); } else if (command == QLatin1String("plain")) { message(args, MarkdownOverride::OFF); } else if (command == QLatin1String("rainbow")) { diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index eb261b02..32e3deee 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -40,6 +40,7 @@ enum class MarkdownOverride NOT_SPECIFIED, // no override set ON, OFF, + CMARK, }; class InputVideoSurface final : public QAbstractVideoSurface |