1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Linguist of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "messagehighlighter.h"
30
31 #include <QtCore/QTextStream>
32 #include <QtWidgets/QTextEdit>
33
34 QT_BEGIN_NAMESPACE
35
MessageHighlighter(QTextEdit * textEdit)36 MessageHighlighter::MessageHighlighter(QTextEdit *textEdit)
37 : QSyntaxHighlighter(textEdit->document())
38 {
39 QTextCharFormat entityFormat;
40 entityFormat.setForeground(Qt::red);
41 m_formats[Entity] = entityFormat;
42
43 QTextCharFormat tagFormat;
44 tagFormat.setForeground(Qt::darkMagenta);
45 m_formats[Tag] = tagFormat;
46
47 QTextCharFormat commentFormat;
48 commentFormat.setForeground(Qt::gray);
49 commentFormat.setFontItalic(true);
50 m_formats[Comment] = commentFormat;
51
52 QTextCharFormat attributeFormat;
53 attributeFormat.setForeground(Qt::black);
54 attributeFormat.setFontItalic(true);
55 m_formats[Attribute] = attributeFormat;
56
57 QTextCharFormat valueFormat;
58 valueFormat.setForeground(Qt::blue);
59 m_formats[Value] = valueFormat;
60
61 QTextCharFormat acceleratorFormat;
62 acceleratorFormat.setFontUnderline(true);
63 m_formats[Accelerator] = acceleratorFormat;
64
65 QTextCharFormat variableFormat;
66 variableFormat.setForeground(Qt::blue);
67 m_formats[Variable] = variableFormat;
68
69 rehighlight();
70 }
71
highlightBlock(const QString & text)72 void MessageHighlighter::highlightBlock(const QString &text)
73 {
74 static const QLatin1Char tab = QLatin1Char('\t');
75 static const QLatin1Char space = QLatin1Char(' ');
76 static const QLatin1Char amp = QLatin1Char('&');
77 static const QLatin1Char endTag = QLatin1Char('>');
78 static const QLatin1Char quot = QLatin1Char('"');
79 static const QLatin1Char apos = QLatin1Char('\'');
80 static const QLatin1Char semicolon = QLatin1Char(';');
81 static const QLatin1Char equals = QLatin1Char('=');
82 static const QLatin1Char percent = QLatin1Char('%');
83 static const QLatin1String startComment = QLatin1String("<!--");
84 static const QLatin1String endComment = QLatin1String("-->");
85 static const QLatin1String endElement = QLatin1String("/>");
86
87 int state = previousBlockState();
88 int len = text.length();
89 int start = 0;
90 int pos = 0;
91
92 while (pos < len) {
93 switch (state) {
94 case NormalState:
95 default:
96 while (pos < len) {
97 QChar ch = text.at(pos);
98 if (ch == QLatin1Char('<')) {
99 if (text.mid(pos, 4) == startComment) {
100 state = InComment;
101 } else {
102 state = InTag;
103 start = pos;
104 while (pos < len && text.at(pos) != space
105 && text.at(pos) != endTag
106 && text.at(pos) != tab
107 && text.mid(pos, 2) != endElement)
108 ++pos;
109 if (text.mid(pos, 2) == endElement)
110 ++pos;
111 setFormat(start, pos - start,
112 m_formats[Tag]);
113 break;
114 }
115 break;
116 } else if (ch == amp && pos + 1 < len) {
117 // Default is Accelerator
118 if (text.at(pos + 1).isLetterOrNumber())
119 setFormat(pos + 1, 1, m_formats[Accelerator]);
120
121 // When a semicolon follows assume an Entity
122 start = pos;
123 ch = text.at(++pos);
124 while (pos + 1 < len && ch != semicolon && ch.isLetterOrNumber())
125 ch = text.at(++pos);
126 if (ch == semicolon)
127 setFormat(start, pos - start + 1, m_formats[Entity]);
128 } else if (ch == percent) {
129 start = pos;
130 // %[1-9]*
131 for (++pos; pos < len && text.at(pos).isDigit(); ++pos) {}
132 // %n
133 if (pos < len && pos == start + 1 && text.at(pos) == QLatin1Char('n'))
134 ++pos;
135 setFormat(start, pos - start, m_formats[Variable]);
136 } else {
137 // No tag, comment or entity started, continue...
138 ++pos;
139 }
140 }
141 break;
142 case InComment:
143 start = pos;
144 while (pos < len) {
145 if (text.mid(pos, 3) == endComment) {
146 pos += 3;
147 state = NormalState;
148 break;
149 } else {
150 ++pos;
151 }
152 }
153 setFormat(start, pos - start, m_formats[Comment]);
154 break;
155 case InTag:
156 QChar quote = QChar::Null;
157 while (pos < len) {
158 QChar ch = text.at(pos);
159 if (quote.isNull()) {
160 start = pos;
161 if (ch == apos || ch == quot) {
162 quote = ch;
163 } else if (ch == endTag) {
164 ++pos;
165 setFormat(start, pos - start, m_formats[Tag]);
166 state = NormalState;
167 break;
168 } else if (text.mid(pos, 2) == endElement) {
169 pos += 2;
170 setFormat(start, pos - start, m_formats[Tag]);
171 state = NormalState;
172 break;
173 } else if (ch != space && text.at(pos) != tab) {
174 // Tag not ending, not a quote and no whitespace, so
175 // we must be dealing with an attribute.
176 ++pos;
177 while (pos < len && text.at(pos) != space
178 && text.at(pos) != tab
179 && text.at(pos) != equals)
180 ++pos;
181 setFormat(start, pos - start, m_formats[Attribute]);
182 start = pos;
183 }
184 } else if (ch == quote) {
185 quote = QChar::Null;
186
187 // Anything quoted is a value
188 setFormat(start, pos - start, m_formats[Value]);
189 }
190 ++pos;
191 }
192 break;
193 }
194 }
195 setCurrentBlockState(state);
196 }
197
198 QT_END_NAMESPACE
199