File indexing completed on 2024-05-12 15:50:06

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "syntaxhighlighter.h"
0008 #include "abstracthighlighter_p.h"
0009 #include "definition.h"
0010 #include "foldingregion.h"
0011 #include "format.h"
0012 #include "state.h"
0013 #include "theme.h"
0014 
0015 Q_DECLARE_METATYPE(QTextBlock)
0016 
0017 using namespace KSyntaxHighlighting;
0018 
0019 namespace KSyntaxHighlighting
0020 {
0021 class TextBlockUserData : public QTextBlockUserData
0022 {
0023 public:
0024     State state;
0025     QVector<FoldingRegion> foldingRegions;
0026 };
0027 
0028 class SyntaxHighlighterPrivate : public AbstractHighlighterPrivate
0029 {
0030 public:
0031     static FoldingRegion foldingRegion(const QTextBlock &startBlock);
0032     QVector<FoldingRegion> foldingRegions;
0033 };
0034 
0035 }
0036 
0037 FoldingRegion SyntaxHighlighterPrivate::foldingRegion(const QTextBlock &startBlock)
0038 {
0039     const auto data = dynamic_cast<TextBlockUserData *>(startBlock.userData());
0040     if (!data) {
0041         return FoldingRegion();
0042     }
0043     for (int i = data->foldingRegions.size() - 1; i >= 0; --i) {
0044         if (data->foldingRegions.at(i).type() == FoldingRegion::Begin) {
0045             return data->foldingRegions.at(i);
0046         }
0047     }
0048     return FoldingRegion();
0049 }
0050 
0051 SyntaxHighlighter::SyntaxHighlighter(QObject *parent)
0052     : QSyntaxHighlighter(parent)
0053     , AbstractHighlighter(new SyntaxHighlighterPrivate)
0054 {
0055     qRegisterMetaType<QTextBlock>();
0056 }
0057 
0058 SyntaxHighlighter::SyntaxHighlighter(QTextDocument *document)
0059     : QSyntaxHighlighter(document)
0060     , AbstractHighlighter(new SyntaxHighlighterPrivate)
0061 {
0062     qRegisterMetaType<QTextBlock>();
0063 }
0064 
0065 SyntaxHighlighter::~SyntaxHighlighter()
0066 {
0067 }
0068 
0069 void SyntaxHighlighter::setDefinition(const Definition &def)
0070 {
0071     const auto needsRehighlight = definition() != def;
0072     AbstractHighlighter::setDefinition(def);
0073     if (needsRehighlight) {
0074         rehighlight();
0075     }
0076 }
0077 
0078 bool SyntaxHighlighter::startsFoldingRegion(const QTextBlock &startBlock) const
0079 {
0080     return SyntaxHighlighterPrivate::foldingRegion(startBlock).type() == FoldingRegion::Begin;
0081 }
0082 
0083 QTextBlock SyntaxHighlighter::findFoldingRegionEnd(const QTextBlock &startBlock) const
0084 {
0085     const auto region = SyntaxHighlighterPrivate::foldingRegion(startBlock);
0086 
0087     auto block = startBlock;
0088     int depth = 1;
0089     while (block.isValid()) {
0090         block = block.next();
0091         const auto data = dynamic_cast<TextBlockUserData *>(block.userData());
0092         if (!data) {
0093             continue;
0094         }
0095         for (auto it = data->foldingRegions.constBegin(); it != data->foldingRegions.constEnd(); ++it) {
0096             if ((*it).id() != region.id()) {
0097                 continue;
0098             }
0099             if ((*it).type() == FoldingRegion::End) {
0100                 --depth;
0101             } else if ((*it).type() == FoldingRegion::Begin) {
0102                 ++depth;
0103             }
0104             if (depth == 0) {
0105                 return block;
0106             }
0107         }
0108     }
0109 
0110     return QTextBlock();
0111 }
0112 
0113 void SyntaxHighlighter::highlightBlock(const QString &text)
0114 {
0115     Q_D(SyntaxHighlighter);
0116 
0117     State state;
0118     if (currentBlock().position() > 0) {
0119         const auto prevBlock = currentBlock().previous();
0120         const auto prevData = dynamic_cast<TextBlockUserData *>(prevBlock.userData());
0121         if (prevData) {
0122             state = prevData->state;
0123         }
0124     }
0125     d->foldingRegions.clear();
0126     state = highlightLine(text, state);
0127 
0128     auto data = dynamic_cast<TextBlockUserData *>(currentBlockUserData());
0129     if (!data) { // first time we highlight this
0130         data = new TextBlockUserData;
0131         data->state = state;
0132         data->foldingRegions = d->foldingRegions;
0133         setCurrentBlockUserData(data);
0134         return;
0135     }
0136 
0137     if (data->state == state && data->foldingRegions == d->foldingRegions) { // we ended up in the same state, so we are done here
0138         return;
0139     }
0140     data->state = state;
0141     data->foldingRegions = d->foldingRegions;
0142 
0143     const auto nextBlock = currentBlock().next();
0144     if (nextBlock.isValid()) {
0145         QMetaObject::invokeMethod(this, "rehighlightBlock", Qt::QueuedConnection, Q_ARG(QTextBlock, nextBlock));
0146     }
0147 }
0148 
0149 void SyntaxHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format)
0150 {
0151     if (length == 0) {
0152         return;
0153     }
0154 
0155     QTextCharFormat tf;
0156     // always set the foreground color to avoid palette issues
0157     tf.setForeground(format.textColor(theme()));
0158 
0159     if (format.hasBackgroundColor(theme())) {
0160         tf.setBackground(format.backgroundColor(theme()));
0161     }
0162     if (format.isBold(theme())) {
0163         tf.setFontWeight(QFont::Bold);
0164     }
0165     if (format.isItalic(theme())) {
0166         tf.setFontItalic(true);
0167     }
0168     if (format.isUnderline(theme())) {
0169         tf.setFontUnderline(true);
0170     }
0171     if (format.isStrikeThrough(theme())) {
0172         tf.setFontStrikeOut(true);
0173     }
0174 
0175     QSyntaxHighlighter::setFormat(offset, length, tf);
0176 }
0177 
0178 void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion region)
0179 {
0180     Q_UNUSED(offset);
0181     Q_UNUSED(length);
0182     Q_D(SyntaxHighlighter);
0183 
0184     if (region.type() == FoldingRegion::Begin) {
0185         d->foldingRegions.push_back(region);
0186     }
0187 
0188     if (region.type() == FoldingRegion::End) {
0189         for (int i = d->foldingRegions.size() - 1; i >= 0; --i) {
0190             if (d->foldingRegions.at(i).id() != region.id() || d->foldingRegions.at(i).type() != FoldingRegion::Begin) {
0191                 continue;
0192             }
0193             d->foldingRegions.remove(i);
0194             return;
0195         }
0196         d->foldingRegions.push_back(region);
0197     }
0198 }
0199 
0200 #include "moc_syntaxhighlighter.cpp"