1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "messagehighlighter.h"
5
6#include <QtCore/QTextStream>
7#include <QtWidgets/QTextEdit>
8
9QT_BEGIN_NAMESPACE
10
11MessageHighlighter::MessageHighlighter(QTextEdit *textEdit)
12 : QSyntaxHighlighter(textEdit->document())
13{
14 QTextCharFormat entityFormat;
15 entityFormat.setForeground(Qt::red);
16 m_formats[Entity] = entityFormat;
17
18 QTextCharFormat tagFormat;
19 tagFormat.setForeground(Qt::darkMagenta);
20 m_formats[Tag] = tagFormat;
21
22 QTextCharFormat commentFormat;
23 commentFormat.setForeground(Qt::gray);
24 commentFormat.setFontItalic(true);
25 m_formats[Comment] = commentFormat;
26
27 QTextCharFormat attributeFormat;
28 attributeFormat.setForeground(Qt::black);
29 attributeFormat.setFontItalic(true);
30 m_formats[Attribute] = attributeFormat;
31
32 QTextCharFormat valueFormat;
33 valueFormat.setForeground(Qt::blue);
34 m_formats[Value] = valueFormat;
35
36 QTextCharFormat acceleratorFormat;
37 acceleratorFormat.setFontUnderline(true);
38 m_formats[Accelerator] = acceleratorFormat;
39
40 QTextCharFormat variableFormat;
41 variableFormat.setForeground(Qt::blue);
42 m_formats[Variable] = variableFormat;
43
44 rehighlight();
45}
46
47void MessageHighlighter::highlightBlock(const QString &text)
48{
49 static const QLatin1Char tab = QLatin1Char('\t');
50 static const QLatin1Char space = QLatin1Char(' ');
51 static const QLatin1Char amp = QLatin1Char('&');
52 static const QLatin1Char endTag = QLatin1Char('>');
53 static const QLatin1Char quot = QLatin1Char('"');
54 static const QLatin1Char apos = QLatin1Char('\'');
55 static const QLatin1Char semicolon = QLatin1Char(';');
56 static const QLatin1Char equals = QLatin1Char('=');
57 static const QLatin1Char percent = QLatin1Char('%');
58 static const QLatin1String startComment = QLatin1String("<!--");
59 static const QLatin1String endComment = QLatin1String("-->");
60 static const QLatin1String endElement = QLatin1String("/>");
61
62 int state = previousBlockState();
63 int len = text.size();
64 int start = 0;
65 int pos = 0;
66
67 while (pos < len) {
68 switch (state) {
69 case NormalState:
70 default:
71 while (pos < len) {
72 QChar ch = text.at(i: pos);
73 if (ch == QLatin1Char('<')) {
74 if (text.mid(position: pos, n: 4) == startComment) {
75 state = InComment;
76 } else {
77 state = InTag;
78 start = pos;
79 while (pos < len && text.at(i: pos) != space
80 && text.at(i: pos) != endTag
81 && text.at(i: pos) != tab
82 && text.mid(position: pos, n: 2) != endElement)
83 ++pos;
84 if (text.mid(position: pos, n: 2) == endElement)
85 ++pos;
86 setFormat(start, count: pos - start,
87 format: m_formats[Tag]);
88 break;
89 }
90 break;
91 } else if (ch == amp && pos + 1 < len) {
92 // Default is Accelerator
93 if (text.at(i: pos + 1).isLetterOrNumber())
94 setFormat(start: pos + 1, count: 1, format: m_formats[Accelerator]);
95
96 // When a semicolon follows assume an Entity
97 start = pos;
98 ch = text.at(i: ++pos);
99 while (pos + 1 < len && ch != semicolon && ch.isLetterOrNumber())
100 ch = text.at(i: ++pos);
101 if (ch == semicolon)
102 setFormat(start, count: pos - start + 1, format: m_formats[Entity]);
103 } else if (ch == percent) {
104 start = pos;
105 // %[1-9]*
106 for (++pos; pos < len && text.at(i: pos).isDigit(); ++pos) {}
107 // %n
108 if (pos < len && pos == start + 1 && text.at(i: pos) == QLatin1Char('n'))
109 ++pos;
110 setFormat(start, count: pos - start, format: m_formats[Variable]);
111 } else {
112 // No tag, comment or entity started, continue...
113 ++pos;
114 }
115 }
116 break;
117 case InComment:
118 start = pos;
119 while (pos < len) {
120 if (text.mid(position: pos, n: 3) == endComment) {
121 pos += 3;
122 state = NormalState;
123 break;
124 } else {
125 ++pos;
126 }
127 }
128 setFormat(start, count: pos - start, format: m_formats[Comment]);
129 break;
130 case InTag:
131 QChar quote = QChar::Null;
132 while (pos < len) {
133 QChar ch = text.at(i: pos);
134 if (quote.isNull()) {
135 start = pos;
136 if (ch == apos || ch == quot) {
137 quote = ch;
138 } else if (ch == endTag) {
139 ++pos;
140 setFormat(start, count: pos - start, format: m_formats[Tag]);
141 state = NormalState;
142 break;
143 } else if (text.mid(position: pos, n: 2) == endElement) {
144 pos += 2;
145 setFormat(start, count: pos - start, format: m_formats[Tag]);
146 state = NormalState;
147 break;
148 } else if (ch != space && text.at(i: pos) != tab) {
149 // Tag not ending, not a quote and no whitespace, so
150 // we must be dealing with an attribute.
151 ++pos;
152 while (pos < len && text.at(i: pos) != space
153 && text.at(i: pos) != tab
154 && text.at(i: pos) != equals)
155 ++pos;
156 setFormat(start, count: pos - start, format: m_formats[Attribute]);
157 start = pos;
158 }
159 } else if (ch == quote) {
160 quote = QChar::Null;
161
162 // Anything quoted is a value
163 setFormat(start, count: pos - start, format: m_formats[Value]);
164 }
165 ++pos;
166 }
167 break;
168 }
169 }
170 setCurrentBlockState(state);
171}
172
173QT_END_NAMESPACE
174

source code of qttools/src/linguist/linguist/messagehighlighter.cpp