29CppSyntaxHighlighter::CppSyntaxHighlighter(QTextDocument *parent)
30 : QSyntaxHighlighter(parent),
32 QStringLiteral(
"\\b([A-Za-z_][A-Za-z0-9_]*)\\s*(?=\\()")) {
33 commentFormat_.setForeground(QColor(110, 118, 129));
34 commentFormat_.setFontItalic(
true);
36 stringFormat_.setForeground(QColor(10, 122, 76));
38 keywordFormat_.setForeground(QColor(20, 80, 160));
39 keywordFormat_.setFontWeight(QFont::Bold);
41 functionFormat_.setForeground(QColor(146, 64, 14));
42 functionFormat_.setFontUnderline(
true);
44 preprocessorFormat_.setForeground(QColor(130, 60, 130));
46 static const QStringList keywords = {
47 QStringLiteral(
"alignas"), QStringLiteral(
"alignof"),
48 QStringLiteral(
"asm"), QStringLiteral(
"auto"),
49 QStringLiteral(
"bool"), QStringLiteral(
"break"),
50 QStringLiteral(
"case"), QStringLiteral(
"catch"),
51 QStringLiteral(
"char"), QStringLiteral(
"class"),
52 QStringLiteral(
"const"), QStringLiteral(
"constexpr"),
53 QStringLiteral(
"consteval"), QStringLiteral(
"constinit"),
54 QStringLiteral(
"continue"), QStringLiteral(
"decltype"),
55 QStringLiteral(
"default"), QStringLiteral(
"delete"),
56 QStringLiteral(
"do"), QStringLiteral(
"double"),
57 QStringLiteral(
"else"), QStringLiteral(
"enum"),
58 QStringLiteral(
"explicit"), QStringLiteral(
"extern"),
59 QStringLiteral(
"false"), QStringLiteral(
"float"),
60 QStringLiteral(
"for"), QStringLiteral(
"friend"),
61 QStringLiteral(
"goto"), QStringLiteral(
"if"),
62 QStringLiteral(
"inline"), QStringLiteral(
"int"),
63 QStringLiteral(
"long"), QStringLiteral(
"mutable"),
64 QStringLiteral(
"namespace"), QStringLiteral(
"new"),
65 QStringLiteral(
"noexcept"), QStringLiteral(
"nullptr"),
66 QStringLiteral(
"operator"), QStringLiteral(
"private"),
67 QStringLiteral(
"protected"), QStringLiteral(
"public"),
68 QStringLiteral(
"register"), QStringLiteral(
"reinterpret_cast"),
69 QStringLiteral(
"requires"), QStringLiteral(
"return"),
70 QStringLiteral(
"short"), QStringLiteral(
"signed"),
71 QStringLiteral(
"sizeof"), QStringLiteral(
"static"),
72 QStringLiteral(
"static_assert"), QStringLiteral(
"struct"),
73 QStringLiteral(
"switch"), QStringLiteral(
"template"),
74 QStringLiteral(
"this"), QStringLiteral(
"thread_local"),
75 QStringLiteral(
"throw"), QStringLiteral(
"true"),
76 QStringLiteral(
"try"), QStringLiteral(
"typedef"),
77 QStringLiteral(
"typename"), QStringLiteral(
"union"),
78 QStringLiteral(
"unsigned"), QStringLiteral(
"using"),
79 QStringLiteral(
"virtual"), QStringLiteral(
"void"),
80 QStringLiteral(
"volatile"), QStringLiteral(
"while")};
82 keywordPatterns_.reserve(
static_cast<std::size_t
>(keywords.size()));
83 for (
const QString &keyword : keywords) {
84 keywordPatterns_.push_back(
85 QRegularExpression(QStringLiteral(
"\\b%1\\b").arg(keyword)));
88 nonFunctionWords_ = QSet<QString>(
89 keywords.begin(), keywords.end());
92void CppSyntaxHighlighter::highlightBlock(
const QString &text) {
93 std::vector<bool> masked(
static_cast<std::size_t
>(text.size()),
false);
95 int state = previousBlockState();
96 setCurrentBlockState(NormalState);
98 if (state == InBlockCommentState) {
99 int end = text.indexOf(QStringLiteral(
"*/"));
101 markCommentRange(text, 0, text.size(), &masked);
102 setCurrentBlockState(InBlockCommentState);
105 markCommentRange(text, 0, end + 2, &masked);
109 while (index < text.size()) {
110 const QChar ch = text.at(index);
112 if (ch ==
'"' || ch ==
'\'') {
113 int nextIndex = index + 1;
114 markStringOrCharLiteral(text, index, &masked, &nextIndex);
119 if (ch ==
'/' && index + 1 < text.size()) {
120 const QChar next = text.at(index + 1);
122 markCommentRange(text, index, text.size() - index, &masked);
126 int end = text.indexOf(QStringLiteral(
"*/"), index + 2);
128 markCommentRange(text, index, text.size() - index, &masked);
129 setCurrentBlockState(InBlockCommentState);
132 markCommentRange(text, index, end - index + 2, &masked);
141 applyKeywordRules(text, masked);
142 applyFunctionRules(text, masked);
143 applyPreprocessorRules(text, &masked);
146void CppSyntaxHighlighter::markCommentRange(
const QString &text,
int start,
148 std::vector<bool> *masked) {
149 if (!masked || start < 0 || length <= 0 || start >= text.size()) {
153 int safeLength = qMin(length, text.size() - start);
154 setFormat(start, safeLength, commentFormat_);
155 for (
int i = start; i < start + safeLength; ++i) {
156 (*masked)[
static_cast<std::size_t
>(i)] =
true;
160void CppSyntaxHighlighter::markStringOrCharLiteral(
const QString &text,
162 std::vector<bool> *masked,
164 if (!masked || !nextIndex || start < 0 || start >= text.size()) {
168 const QChar quote = text.at(start);
170 bool escaped =
false;
172 while (end < text.size()) {
173 const QChar ch = text.at(end);
176 }
else if (ch ==
'\\') {
178 }
else if (ch == quote) {
185 int length = end - start;
187 *nextIndex = start + 1;
191 setFormat(start, length, stringFormat_);
192 for (
int i = start; i < start + length && i < text.size(); ++i) {
193 (*masked)[
static_cast<std::size_t
>(i)] =
true;
198bool CppSyntaxHighlighter::isUnmaskedRange(
const std::vector<bool> &masked,
199 int start,
int length)
const {
200 if (start < 0 || length <= 0) {
203 for (
int i = start; i < start + length; ++i) {
204 if (i >=
static_cast<int>(masked.size()) ||
205 masked[
static_cast<std::size_t
>(i)]) {
212void CppSyntaxHighlighter::applyKeywordRules(
const QString &text,
213 const std::vector<bool> &masked) {
214 for (
const QRegularExpression &pattern : keywordPatterns_) {
215 auto it = pattern.globalMatch(text);
216 while (it.hasNext()) {
217 const auto match = it.next();
218 const int start = match.capturedStart();
219 const int length = match.capturedLength();
220 if (isUnmaskedRange(masked, start, length)) {
221 setFormat(start, length, keywordFormat_);
227void CppSyntaxHighlighter::applyFunctionRules(
const QString &text,
228 const std::vector<bool> &masked) {
229 auto it = functionPattern_.globalMatch(text);
230 while (it.hasNext()) {
231 const auto match = it.next();
232 const QString identifier = match.captured(1);
233 if (identifier.isEmpty() || nonFunctionWords_.contains(identifier)) {
236 const int start = match.capturedStart(1);
237 const int length = match.capturedLength(1);
238 if (isUnmaskedRange(masked, start, length)) {
239 setFormat(start, length, functionFormat_);
244void CppSyntaxHighlighter::applyPreprocessorRules(
const QString &text,
245 std::vector<bool> *masked) {
252 while (start < text.size() && text.at(start).isSpace()) {
257 if (start >= text.size() || text.at(start) !=
'#') {
262 int directiveStart = start + 1;
263 while (directiveStart < text.size() && text.at(directiveStart).isSpace()) {
266 int directiveEnd = directiveStart;
267 while (directiveEnd < text.size() && text.at(directiveEnd).isLetterOrNumber()) {
272 if (directiveEnd > start) {
273 for (
int i = start; i < directiveEnd; ++i) {
274 if (!(*masked)[
static_cast<std::size_t
>(i)]) {
275 setFormat(i, 1, preprocessorFormat_);
281 const QStringView directive =
282 QStringView(text).mid(directiveStart, directiveEnd - directiveStart);
283 if (directive == QStringLiteral(
"include")) {
284 int pos = directiveEnd;
285 while (pos < text.size() && text.at(pos).isSpace()) {
288 if (pos < text.size() && text.at(pos) ==
'<') {
289 int angleEnd = text.indexOf(
'>', pos + 1);
291 int length = angleEnd - pos + 1;
292 setFormat(pos, length, stringFormat_);
293 for (
int i = pos; i < pos + length; ++i) {
294 (*masked)[
static_cast<std::size_t
>(i)] =
true;
Lightweight C/C++ syntax highlighter for SourceCodeView.