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
This commit is contained in:
Nicolas Werner 2023-01-31 18:22:12 +01:00
parent 9f529075f0
commit 0c3d46795b
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
8 changed files with 48 additions and 15 deletions

View File

@ -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:

View File

@ -40,6 +40,7 @@ public:
ResetState,
RotateMegolmSession,
Md,
Cmark,
Plain,
Rainbow,
RainbowMe,

View File

@ -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;

View File

@ -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);
};

View File

@ -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>"))) {

View File

@ -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

View File

@ -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")) {

View File

@ -40,6 +40,7 @@ enum class MarkdownOverride
NOT_SPECIFIED, // no override set
ON,
OFF,
CMARK,
};
class InputVideoSurface final : public QAbstractVideoSurface