2023-02-06 12:52:03 +00:00
|
|
|
#include "StatusQ/statussyntaxhighlighter.h"
|
2023-01-06 22:39:19 +00:00
|
|
|
|
2021-08-05 15:25:45 +00:00
|
|
|
#include <QQuickTextDocument>
|
2023-11-07 11:32:21 +00:00
|
|
|
#include <QUrl>
|
2021-08-05 15:25:45 +00:00
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
StatusSyntaxHighlighter::StatusSyntaxHighlighter(QObject* parent)
|
2021-08-05 15:25:45 +00:00
|
|
|
: QSyntaxHighlighter(parent)
|
2023-01-06 22:39:19 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::componentComplete()
|
2021-08-05 15:25:45 +00:00
|
|
|
{
|
|
|
|
HighlightingRule rule;
|
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
//BOLD
|
2021-08-05 15:25:45 +00:00
|
|
|
singlelineBoldFormat.setFontWeight(QFont::Bold);
|
2022-06-10 15:50:49 +00:00
|
|
|
rule.pattern = QRegularExpression(QStringLiteral("(\\*\\*(.*?)\\*\\*)|(\\_\\_(.*?)\\_\\_)"));
|
2021-08-05 15:25:45 +00:00
|
|
|
rule.format = singlelineBoldFormat;
|
|
|
|
highlightingRules.append(rule);
|
2023-01-06 22:39:19 +00:00
|
|
|
//BOLD
|
2021-08-05 15:25:45 +00:00
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
//ITALIC
|
2021-08-05 15:25:45 +00:00
|
|
|
singleLineItalicFormat.setFontItalic(true);
|
2022-06-10 15:50:49 +00:00
|
|
|
rule.pattern = QRegularExpression(QStringLiteral("(\\*(.*?)\\*)|(\\_(.*?)\\_)"));
|
2021-08-05 15:25:45 +00:00
|
|
|
rule.format = singleLineItalicFormat;
|
|
|
|
highlightingRules.append(rule);
|
2023-01-06 22:39:19 +00:00
|
|
|
//ITALIC
|
2021-08-05 15:25:45 +00:00
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
//STRIKETHROUGH
|
2021-08-05 15:25:45 +00:00
|
|
|
singleLineStrikeThroughFormat.setFontStrikeOut(true);
|
2022-06-10 15:50:49 +00:00
|
|
|
rule.pattern = QRegularExpression(QStringLiteral("\\~\\~(.*?)\\~\\~"));
|
2021-08-05 15:25:45 +00:00
|
|
|
rule.format = singleLineStrikeThroughFormat;
|
|
|
|
highlightingRules.append(rule);
|
2023-01-06 22:39:19 +00:00
|
|
|
//STRIKETHROUGH
|
|
|
|
|
|
|
|
//CODE (`foo`)
|
|
|
|
codeFormat.setFontFamily(QStringLiteral("Roboto Mono"));
|
|
|
|
codeFormat.setBackground(m_codeBackgroundColor);
|
|
|
|
codeFormat.setForeground(m_codeForegroundColor);
|
|
|
|
rule.pattern = QRegularExpression(QStringLiteral("\\`{1}(.+)\\`{1}"),
|
|
|
|
// to not match single backtick pair inside a triple backtick block below
|
|
|
|
QRegularExpression::InvertedGreedinessOption);
|
|
|
|
rule.format = codeFormat;
|
|
|
|
highlightingRules.append(rule);
|
|
|
|
//CODE
|
2021-08-05 15:25:45 +00:00
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
//CODEBLOCK (```\nfoo\nbar```)
|
|
|
|
rule.pattern = QRegularExpression(QStringLiteral("\\`{3}(.+)\\`{3}"));
|
|
|
|
rule.format = codeFormat;
|
2021-08-05 15:25:45 +00:00
|
|
|
highlightingRules.append(rule);
|
2023-01-06 22:39:19 +00:00
|
|
|
//CODEBLOCK
|
2023-11-07 11:32:21 +00:00
|
|
|
|
|
|
|
//HYPERLINKS
|
|
|
|
//QRegularExpression to match any hyperlink in m_hyperlinks
|
|
|
|
hyperlinkFormat.setForeground(m_hyperlinkColor);
|
|
|
|
rule.pattern = hyperlinksRegularExpression();
|
|
|
|
rule.format = hyperlinkFormat;
|
|
|
|
rule.matchType = QRegularExpression::NormalMatch;
|
|
|
|
highlightingRules.append(rule);
|
|
|
|
|
|
|
|
const int hyperlinksRuleIndex = highlightingRules.size() - 1;
|
|
|
|
|
|
|
|
//HIGHLIGHTED
|
|
|
|
highlightedHyperlinkFormat.setForeground(m_hyperlinkColor);
|
|
|
|
highlightedHyperlinkFormat.setBackground(m_hyperlinkHoverColor);
|
|
|
|
rule.pattern = highlightedHyperlinkRegularExpression();
|
|
|
|
rule.format = highlightedHyperlinkFormat;
|
|
|
|
rule.matchType = QRegularExpression::NormalMatch;
|
|
|
|
highlightingRules.append(rule);
|
|
|
|
|
|
|
|
const int highlightedHyperlinkRuleIndex = highlightingRules.size() - 1;
|
|
|
|
|
|
|
|
connect(this, &StatusSyntaxHighlighter::hyperlinksChanged, this, [hyperlinksRuleIndex, this](){
|
|
|
|
highlightingRules[hyperlinksRuleIndex].pattern = hyperlinksRegularExpression();
|
|
|
|
rehighlight();
|
|
|
|
});
|
|
|
|
connect(this, &StatusSyntaxHighlighter::hyperlinkColorChanged, this, [hyperlinksRuleIndex, this](){
|
|
|
|
hyperlinkFormat.setForeground(m_hyperlinkColor);
|
|
|
|
highlightedHyperlinkFormat.setForeground(m_hyperlinkColor);
|
|
|
|
highlightingRules[hyperlinksRuleIndex].format = hyperlinkFormat;
|
|
|
|
rehighlight();
|
|
|
|
});
|
|
|
|
|
|
|
|
connect(this, &StatusSyntaxHighlighter::highlightedHyperlinkChanged, this, [highlightedHyperlinkRuleIndex, this](){
|
|
|
|
highlightingRules[highlightedHyperlinkRuleIndex].pattern = highlightedHyperlinkRegularExpression();
|
|
|
|
rehighlight();
|
|
|
|
});
|
|
|
|
connect(this, &StatusSyntaxHighlighter::hyperlinkHoverColorChanged, this, [highlightedHyperlinkRuleIndex, this](){
|
|
|
|
highlightedHyperlinkFormat.setBackground(m_hyperlinkHoverColor);
|
|
|
|
highlightingRules[highlightedHyperlinkRuleIndex].format = highlightedHyperlinkFormat;
|
|
|
|
rehighlight();
|
|
|
|
});
|
2021-08-05 15:25:45 +00:00
|
|
|
}
|
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
void StatusSyntaxHighlighter::highlightBlock(const QString& text)
|
2021-08-05 15:25:45 +00:00
|
|
|
{
|
2023-01-06 22:39:19 +00:00
|
|
|
for(const HighlightingRule& rule : qAsConst(highlightingRules))
|
|
|
|
{
|
2023-11-07 11:32:21 +00:00
|
|
|
if(rule.pattern.pattern() == QStringLiteral("")) continue;
|
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
QRegularExpressionMatchIterator matchIterator =
|
2023-11-07 11:32:21 +00:00
|
|
|
rule.pattern.globalMatch(text, 0, rule.matchType);
|
2023-01-06 22:39:19 +00:00
|
|
|
while(matchIterator.hasNext())
|
|
|
|
{
|
|
|
|
const QRegularExpressionMatch match = matchIterator.next();
|
2021-08-05 15:25:45 +00:00
|
|
|
setFormat(match.capturedStart(), match.capturedLength(), rule.format);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
QQuickTextDocument* StatusSyntaxHighlighter::quickTextDocument() const
|
|
|
|
{
|
2021-08-05 15:25:45 +00:00
|
|
|
return m_quicktextdocument;
|
|
|
|
}
|
|
|
|
|
2023-01-06 22:39:19 +00:00
|
|
|
void StatusSyntaxHighlighter::setQuickTextDocument(QQuickTextDocument* quickTextDocument)
|
|
|
|
{
|
|
|
|
if(!quickTextDocument) return;
|
|
|
|
if(quickTextDocument == m_quicktextdocument) return;
|
|
|
|
|
2021-08-05 15:25:45 +00:00
|
|
|
m_quicktextdocument = quickTextDocument;
|
2023-01-06 22:39:19 +00:00
|
|
|
setDocument(m_quicktextdocument->textDocument());
|
|
|
|
emit quickTextDocumentChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor StatusSyntaxHighlighter::codeBackgroundColor() const
|
|
|
|
{
|
|
|
|
return m_codeBackgroundColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::setCodeBackgroundColor(const QColor& color)
|
|
|
|
{
|
|
|
|
if(color == m_codeBackgroundColor) return;
|
|
|
|
m_codeBackgroundColor = color;
|
|
|
|
emit codeBackgroundColorChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor StatusSyntaxHighlighter::codeForegroundColor() const
|
|
|
|
{
|
|
|
|
return m_codeForegroundColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::setCodeForegroundColor(const QColor& color)
|
|
|
|
{
|
|
|
|
if(color == m_codeForegroundColor) return;
|
|
|
|
m_codeForegroundColor = color;
|
|
|
|
emit codeForegroundColorChanged();
|
2021-08-05 15:25:45 +00:00
|
|
|
}
|
2023-11-07 11:32:21 +00:00
|
|
|
|
|
|
|
QColor StatusSyntaxHighlighter::hyperlinkColor() const
|
|
|
|
{
|
|
|
|
return m_hyperlinkColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::setHyperlinkColor(const QColor& color)
|
|
|
|
{
|
|
|
|
if(color == m_hyperlinkColor) return;
|
|
|
|
m_hyperlinkColor = color;
|
|
|
|
emit hyperlinkColorChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QColor StatusSyntaxHighlighter::hyperlinkHoverColor() const
|
|
|
|
{
|
|
|
|
return m_hyperlinkHoverColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::setHyperlinkHoverColor(const QColor& color)
|
|
|
|
{
|
|
|
|
if(color == m_hyperlinkHoverColor) return;
|
|
|
|
m_hyperlinkHoverColor = color;
|
|
|
|
emit hyperlinkHoverColorChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList StatusSyntaxHighlighter::hyperlinks() const
|
|
|
|
{
|
|
|
|
return m_hyperlinks;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::setHyperlinks(const QStringList& hyperlinks)
|
|
|
|
{
|
|
|
|
if(hyperlinks == m_hyperlinks) return;
|
|
|
|
m_hyperlinks = hyperlinks;
|
|
|
|
emit hyperlinksChanged();
|
|
|
|
}
|
|
|
|
QString StatusSyntaxHighlighter::highlightedHyperlink() const
|
|
|
|
{
|
|
|
|
return m_highlightedHyperlink;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StatusSyntaxHighlighter::setHighlightedHyperlink(const QString& hyperlink)
|
|
|
|
{
|
|
|
|
if(hyperlink == m_highlightedHyperlink) return;
|
|
|
|
m_highlightedHyperlink = hyperlink;
|
|
|
|
emit highlightedHyperlinkChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegularExpression StatusSyntaxHighlighter::highlightedHyperlinkRegularExpression() const
|
|
|
|
{
|
|
|
|
const auto possibleUrlFormats = getPossibleUrlFormats(QUrl(m_highlightedHyperlink));
|
|
|
|
return buildHyperlinkRegex(possibleUrlFormats);
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegularExpression StatusSyntaxHighlighter::hyperlinksRegularExpression() const
|
|
|
|
{
|
|
|
|
QStringList hyperlinks;
|
|
|
|
for(const QString& hyperlink : qAsConst(m_hyperlinks))
|
|
|
|
{
|
|
|
|
const auto possibleUrlFormats = getPossibleUrlFormats(QUrl(hyperlink));
|
|
|
|
hyperlinks.append(possibleUrlFormats);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buildHyperlinkRegex(hyperlinks);
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList StatusSyntaxHighlighter::getPossibleUrlFormats(const QUrl& url) const
|
|
|
|
{
|
|
|
|
QStringList result;
|
|
|
|
result.append(QRegularExpression::escape(url.toString()));
|
|
|
|
result.append(QRegularExpression::escape(url.toString(QUrl::EncodeUnicode)));
|
|
|
|
result.append(QRegularExpression::escape(url.toString(QUrl::FullyEncoded)));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegularExpression StatusSyntaxHighlighter::buildHyperlinkRegex(QStringList hyperlinks) const
|
|
|
|
{
|
|
|
|
hyperlinks.removeAll(QString());
|
|
|
|
|
|
|
|
if(hyperlinks.isEmpty())
|
|
|
|
return QRegularExpression("(?!)");
|
|
|
|
QString matchHyperlinks = QStringLiteral("(?:^|(?<=\\s))(") + hyperlinks.join("|") + QStringLiteral(")(?:(?=\\s)|$)");
|
|
|
|
auto regex = QRegularExpression(matchHyperlinks, QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption | QRegularExpression::MultilineOption);
|
|
|
|
regex.optimize();
|
|
|
|
return regex;
|
|
|
|
}
|