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