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 Designer 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 "csshighlighter_p.h" |
30 | |
31 | QT_BEGIN_NAMESPACE |
32 | |
33 | namespace qdesigner_internal { |
34 | |
35 | CssHighlighter::CssHighlighter(QTextDocument *document) |
36 | : QSyntaxHighlighter(document) |
37 | { |
38 | } |
39 | |
40 | void CssHighlighter::highlightBlock(const QString& text) |
41 | { |
42 | enum Token { ALNUM, LBRACE, RBRACE, COLON, SEMICOLON, COMMA, QUOTE, SLASH, STAR }; |
43 | static const int transitions[10][9] = { |
44 | { Selector, Property, Selector, Pseudo, Property, Selector, Quote, MaybeComment, Selector }, // Selector |
45 | { Property, Property, Selector, Value, Property, Property, Quote, MaybeComment, Property }, // Property |
46 | { Value, Property, Selector, Value, Property, Value, Quote, MaybeComment, Value }, // Value |
47 | { Pseudo1, Property, Selector, Pseudo2, Selector, Selector, Quote, MaybeComment, Pseudo }, // Pseudo |
48 | { Pseudo1, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo1 }, // Pseudo1 |
49 | { Pseudo2, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo2 }, // Pseudo2 |
50 | { Quote, Quote, Quote, Quote, Quote, Quote, -1, Quote, Quote }, // Quote |
51 | { -1, -1, -1, -1, -1, -1, -1, -1, Comment }, // MaybeComment |
52 | { Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, MaybeCommentEnd }, // Comment |
53 | { Comment, Comment, Comment, Comment, Comment, Comment, Comment, -1, MaybeCommentEnd } // MaybeCommentEnd |
54 | }; |
55 | |
56 | int lastIndex = 0; |
57 | bool lastWasSlash = false; |
58 | int state = previousBlockState(), save_state; |
59 | if (state == -1) { |
60 | // As long as the text is empty, leave the state undetermined |
61 | if (text.isEmpty()) { |
62 | setCurrentBlockState(-1); |
63 | return; |
64 | } |
65 | // The initial state is based on the precense of a : and the absense of a {. |
66 | // This is because Qt style sheets support both a full stylesheet as well as |
67 | // an inline form with just properties. |
68 | state = save_state = (text.indexOf(c: QLatin1Char(':')) > -1 && |
69 | text.indexOf(c: QLatin1Char('{')) == -1) ? Property : Selector; |
70 | } else { |
71 | save_state = state>>16; |
72 | state &= 0x00ff; |
73 | } |
74 | |
75 | if (state == MaybeCommentEnd) { |
76 | state = Comment; |
77 | } else if (state == MaybeComment) { |
78 | state = save_state; |
79 | } |
80 | |
81 | for (int i = 0; i < text.length(); i++) { |
82 | int token = ALNUM; |
83 | const QChar c = text.at(i); |
84 | const char a = c.toLatin1(); |
85 | |
86 | if (state == Quote) { |
87 | if (a == '\\') { |
88 | lastWasSlash = true; |
89 | } else { |
90 | if (a == '\"' && !lastWasSlash) { |
91 | token = QUOTE; |
92 | } |
93 | lastWasSlash = false; |
94 | } |
95 | } else { |
96 | switch (a) { |
97 | case '{': token = LBRACE; break; |
98 | case '}': token = RBRACE; break; |
99 | case ':': token = COLON; break; |
100 | case ';': token = SEMICOLON; break; |
101 | case ',': token = COMMA; break; |
102 | case '\"': token = QUOTE; break; |
103 | case '/': token = SLASH; break; |
104 | case '*': token = STAR; break; |
105 | default: break; |
106 | } |
107 | } |
108 | |
109 | int new_state = transitions[state][token]; |
110 | |
111 | if (new_state != state) { |
112 | bool include_token = new_state == MaybeCommentEnd || (state == MaybeCommentEnd && new_state!= Comment) |
113 | || state == Quote; |
114 | highlight(text, lastIndex, i-lastIndex+include_token, state); |
115 | |
116 | if (new_state == Comment) { |
117 | lastIndex = i-1; // include the slash and star |
118 | } else { |
119 | lastIndex = i + ((token == ALNUM || new_state == Quote) ? 0 : 1); |
120 | } |
121 | } |
122 | |
123 | if (new_state == -1) { |
124 | state = save_state; |
125 | } else if (state <= Pseudo2) { |
126 | save_state = state; |
127 | state = new_state; |
128 | } else { |
129 | state = new_state; |
130 | } |
131 | } |
132 | |
133 | highlight(text, lastIndex, text.length() - lastIndex, state); |
134 | setCurrentBlockState(state + (save_state<<16)); |
135 | } |
136 | |
137 | void CssHighlighter::highlight(const QString &text, int start, int length, int state) |
138 | { |
139 | if (start >= text.length() || length <= 0) |
140 | return; |
141 | |
142 | QTextCharFormat format; |
143 | |
144 | switch (state) { |
145 | case Selector: |
146 | setFormat(start, count: length, color: Qt::darkRed); |
147 | break; |
148 | case Property: |
149 | setFormat(start, count: length, color: Qt::blue); |
150 | break; |
151 | case Value: |
152 | setFormat(start, count: length, color: Qt::black); |
153 | break; |
154 | case Pseudo1: |
155 | setFormat(start, count: length, color: Qt::darkRed); |
156 | break; |
157 | case Pseudo2: |
158 | setFormat(start, count: length, color: Qt::darkRed); |
159 | break; |
160 | case Quote: |
161 | setFormat(start, count: length, color: Qt::darkMagenta); |
162 | break; |
163 | case Comment: |
164 | case MaybeCommentEnd: |
165 | format.setForeground(Qt::darkGreen); |
166 | setFormat(start, count: length, format); |
167 | break; |
168 | default: |
169 | break; |
170 | } |
171 | } |
172 | |
173 | } // namespace qdesigner_internal |
174 | |
175 | QT_END_NAMESPACE |
176 | |