KSyntaxHighlighting

syntaxhighlighter.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: MIT
5*/
6
7#include "syntaxhighlighter.h"
8#include "abstracthighlighter_p.h"
9#include "definition.h"
10#include "definition_p.h"
11#include "foldingregion.h"
12#include "format.h"
13#include "format_p.h"
14#include "state.h"
15#include "theme.h"
16#include "themedata_p.h"
17
18Q_DECLARE_METATYPE(QTextBlock)
19
20using namespace KSyntaxHighlighting;
21
22namespace KSyntaxHighlighting
23{
24class TextBlockUserData : public QTextBlockUserData
25{
26public:
27 State state;
28 QList<FoldingRegion> foldingRegions;
29};
30
31class SyntaxHighlighterPrivate : public AbstractHighlighterPrivate
32{
33public:
34 static FoldingRegion foldingRegion(const QTextBlock &startBlock);
35 void initTextFormat(QTextCharFormat &tf, const Format &format);
36 void computeTextFormats();
37
38 struct TextFormat {
40 /**
41 * id to check that the format belongs to the definition
42 */
43 std::intptr_t ptrId;
44 };
45
46 QList<FoldingRegion> foldingRegions;
47 std::vector<TextFormat> tfs;
48};
49
50}
51
52FoldingRegion SyntaxHighlighterPrivate::foldingRegion(const QTextBlock &startBlock)
53{
54 const auto data = dynamic_cast<TextBlockUserData *>(startBlock.userData());
55 if (!data) {
56 return FoldingRegion();
57 }
58 for (int i = data->foldingRegions.size() - 1; i >= 0; --i) {
59 if (data->foldingRegions.at(i).type() == FoldingRegion::Begin) {
60 return data->foldingRegions.at(i);
61 }
62 }
63 return FoldingRegion();
64}
65
66void SyntaxHighlighterPrivate::initTextFormat(QTextCharFormat &tf, const Format &format)
67{
68 // always set the foreground color to avoid palette issues
69 tf.setForeground(format.textColor(m_theme));
70
71 if (format.hasBackgroundColor(m_theme)) {
72 tf.setBackground(format.backgroundColor(m_theme));
73 }
74 if (format.isBold(m_theme)) {
76 }
77 if (format.isItalic(m_theme)) {
78 tf.setFontItalic(true);
79 }
80 if (format.isUnderline(m_theme)) {
81 tf.setFontUnderline(true);
82 }
83 if (format.isStrikeThrough(m_theme)) {
84 tf.setFontStrikeOut(true);
85 }
86}
87
88void SyntaxHighlighterPrivate::computeTextFormats()
89{
90 auto definitions = m_definition.includedDefinitions();
91 definitions.append(m_definition);
92
93 int maxId = 0;
94 for (const auto &definition : std::as_const(definitions)) {
95 for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
96 maxId = qMax(maxId, format.id());
97 }
98 }
99 tfs.clear();
100 tfs.resize(maxId + 1);
101
102 // initialize tfs
103 for (const auto &definition : std::as_const(definitions)) {
104 for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
105 auto &tf = tfs[format.id()];
106 tf.ptrId = FormatPrivate::ptrId(format);
107 initTextFormat(tf.tf, format);
108 }
109 }
110}
111
112SyntaxHighlighter::SyntaxHighlighter(QObject *parent)
113 : QSyntaxHighlighter(parent)
114 , AbstractHighlighter(new SyntaxHighlighterPrivate)
115{
116 qRegisterMetaType<QTextBlock>();
117}
118
119SyntaxHighlighter::SyntaxHighlighter(QTextDocument *document)
120 : QSyntaxHighlighter(document)
121 , AbstractHighlighter(new SyntaxHighlighterPrivate)
122{
123 qRegisterMetaType<QTextBlock>();
124}
125
126SyntaxHighlighter::~SyntaxHighlighter()
127{
128}
129
131{
133
134 const auto needsRehighlight = d->m_definition != def;
135 if (DefinitionData::get(d->m_definition) != DefinitionData::get(def)) {
136 d->m_definition = def;
137 d->tfs.clear();
138 }
139 if (needsRehighlight) {
140 rehighlight();
141 }
142}
143
145{
147 if (ThemeData::get(d->m_theme) != ThemeData::get(theme)) {
148 d->m_theme = theme;
149 d->tfs.clear();
150 }
151}
152
154{
155 return SyntaxHighlighterPrivate::foldingRegion(startBlock).type() == FoldingRegion::Begin;
156}
157
159{
160 const auto region = SyntaxHighlighterPrivate::foldingRegion(startBlock);
161
162 auto block = startBlock;
163 int depth = 1;
164 while (block.isValid()) {
165 block = block.next();
166 const auto data = dynamic_cast<TextBlockUserData *>(block.userData());
167 if (!data) {
168 continue;
169 }
170 for (const auto &foldingRegion : std::as_const(data->foldingRegions)) {
171 if (foldingRegion.id() != region.id()) {
172 continue;
173 }
174 if (foldingRegion.type() == FoldingRegion::End) {
175 --depth;
176 } else if (foldingRegion.type() == FoldingRegion::Begin) {
177 ++depth;
178 }
179 if (depth == 0) {
180 return block;
181 }
182 }
183 }
184
185 return QTextBlock();
186}
187
188void SyntaxHighlighter::highlightBlock(const QString &text)
189{
191
192 static const State emptyState;
193 const State *previousState = &emptyState;
194 if (currentBlock().position() > 0) {
195 const auto prevBlock = currentBlock().previous();
196 const auto prevData = dynamic_cast<TextBlockUserData *>(prevBlock.userData());
197 if (prevData) {
198 previousState = &prevData->state;
199 }
200 }
201 d->foldingRegions.clear();
202 auto newState = highlightLine(text, *previousState);
203
204 auto data = dynamic_cast<TextBlockUserData *>(currentBlockUserData());
205 if (!data) { // first time we highlight this
206 data = new TextBlockUserData;
207 data->state = std::move(newState);
208 data->foldingRegions = d->foldingRegions;
210 return;
211 }
212
213 if (data->state == newState && data->foldingRegions == d->foldingRegions) { // we ended up in the same state, so we are done here
214 return;
215 }
216 data->state = std::move(newState);
217 data->foldingRegions = d->foldingRegions;
218
219 const auto nextBlock = currentBlock().next();
220 if (nextBlock.isValid()) {
221 QMetaObject::invokeMethod(this, "rehighlightBlock", Qt::QueuedConnection, Q_ARG(QTextBlock, nextBlock));
222 }
223}
224
225void SyntaxHighlighter::applyFormat(int offset, int length, const Format &format)
226{
227 if (length == 0) {
228 return;
229 }
230
232
233 if (Q_UNLIKELY(d->tfs.empty())) {
234 d->computeTextFormats();
235 }
236
237 const auto id = static_cast<std::size_t>(format.id());
238 // This doesn't happen when format comes from the definition.
239 // But as the user can override the function to pass any format, this is a possible scenario.
240 if (id < d->tfs.size() && d->tfs[id].ptrId == FormatPrivate::ptrId(format)) {
241 QSyntaxHighlighter::setFormat(offset, length, d->tfs[id].tf);
242 } else {
244 d->initTextFormat(tf, format);
245 QSyntaxHighlighter::setFormat(offset, length, tf);
246 }
247}
248
249void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion region)
250{
251 Q_UNUSED(offset);
252 Q_UNUSED(length);
254
255 if (region.type() == FoldingRegion::Begin) {
256 d->foldingRegions.push_back(region);
257 }
258
259 if (region.type() == FoldingRegion::End) {
260 for (int i = d->foldingRegions.size() - 1; i >= 0; --i) {
261 if (d->foldingRegions.at(i).id() != region.id() || d->foldingRegions.at(i).type() != FoldingRegion::Begin) {
262 continue;
263 }
264 d->foldingRegions.remove(i);
265 return;
266 }
267 d->foldingRegions.push_back(region);
268 }
269}
270
271#include "moc_syntaxhighlighter.cpp"
Abstract base class for highlighters.
Theme theme() const
Returns the currently selected theme for highlighting.
State highlightLine(QStringView text, const State &state)
Highlight the given line.
Represents a syntax definition.
Definition definition.h:83
Represents a begin or end of a folding region.
@ Begin
Indicates the start of a FoldingRegion.
@ End
Indicates the end of a FoldingRegion.
int id() const
Returns a unique identifier for this folding region.
Type type() const
Returns whether this is the begin or end of a region.
Describes the format to be used for a specific text fragment.
Definition format.h:28
bool isUnderline(const Theme &theme) const
Returns true if the combination of this format and the given theme results in underlined text.
Definition format.cpp:167
QColor textColor(const Theme &theme) const
Returns the foreground color of the combination of this format and the given theme.
Definition format.cpp:124
bool hasBackgroundColor(const Theme &theme) const
Returns true if the combination of this format and the theme theme change the background color compar...
Definition format.cpp:134
bool isBold(const Theme &theme) const
Returns true if the combination of this format and the given theme results in bold text formatting.
Definition format.cpp:149
QColor backgroundColor(const Theme &theme) const
Returns the background color of the combination of this format and the given theme.
Definition format.cpp:139
int id() const
Returns a unique identifier of this format.
Definition format.cpp:100
bool isItalic(const Theme &theme) const
Returns true if the combination of this format and the given theme results in italic text formatting.
Definition format.cpp:158
bool isStrikeThrough(const Theme &theme) const
Returns true if the combination of this format and the given theme results in struck through text.
Definition format.cpp:176
Opaque handle to the state of the highlighting engine.
Definition state.h:30
A QSyntaxHighlighter implementation for use with QTextDocument.
QTextBlock findFoldingRegionEnd(const QTextBlock &startBlock) const
Finds the end of the folding region starting at startBlock.
void applyFolding(int offset, int length, FoldingRegion region) override
Reimplement this to apply folding to your output.
void setTheme(const Theme &theme) override
Sets the theme used for highlighting.
bool startsFoldingRegion(const QTextBlock &startBlock) const
Returns whether there is a folding region beginning at startBlock.
void applyFormat(int offset, int length, const Format &format) override
Reimplement this to apply formats to your output.
void setDefinition(const Definition &def) override
Sets the syntax definition used for highlighting.
Color theme definition used for highlighting.
Definition theme.h:65
Syntax highlighting engine for Kate syntax definitions.
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
QTextBlock currentBlock() const const
QTextBlockUserData * currentBlockUserData() const const
QTextCharFormat format(int position) const const
void setCurrentBlockUserData(QTextBlockUserData *data)
void setFormat(int start, int count, const QColor &color)
QueuedConnection
TextFormat
QTextBlock next() const const
QTextBlock previous() const const
QTextBlockUserData * userData() const const
void setFontItalic(bool italic)
void setFontStrikeOut(bool strikeOut)
void setFontUnderline(bool underline)
void setFontWeight(int weight)
void setBackground(const QBrush &brush)
void setForeground(const QBrush &brush)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:04 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.