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"