1 | /* |
---|---|
2 | Copyright (c) 2012 Montel Laurent <montel@kde.org> |
3 | |
4 | based on code from qt-labs-graphics-dojo/htmleditor/highlighter.* |
5 | |
6 | This library is free software; you can redistribute it and/or modify it |
7 | under the terms of the GNU Library General Public License as published by |
8 | the Free Software Foundation; either version 2 of the License, or (at your |
9 | option) any later version. |
10 | |
11 | This library is distributed in the hope that it will be useful, but WITHOUT |
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
14 | License for more details. |
15 | |
16 | You should have received a copy of the GNU Library General Public License |
17 | along with this library; see the file COPYING.LIB. If not, write to the |
18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | 02110-1301, USA. |
20 | |
21 | */ |
22 | |
23 | #include "htmlhighlighter.h" |
24 | |
25 | namespace KPIMTextEdit { |
26 | |
27 | class HtmlHighlighterPrivate |
28 | { |
29 | public: |
30 | enum Construct { |
31 | DocType, |
32 | Entity, |
33 | Tag, |
34 | Comment, |
35 | AttributeName, |
36 | AttributeValue |
37 | }; |
38 | |
39 | enum State { |
40 | State_Text = -1, |
41 | State_DocType, |
42 | State_Comment, |
43 | State_TagStart, |
44 | State_TagName, |
45 | State_InsideTag, |
46 | State_AttributeName, |
47 | State_SingleQuote, |
48 | State_DoubleQuote, |
49 | State_AttributeValue |
50 | }; |
51 | |
52 | HtmlHighlighterPrivate() |
53 | { |
54 | colors[DocType] = QColor( 192, 192, 192 ); |
55 | colors[Entity] = QColor( 128, 128, 128 ); |
56 | colors[Tag] = QColor( 136, 18, 128 ); |
57 | colors[Comment] = QColor( 35, 110, 37 ); |
58 | colors[AttributeName] = QColor( 153, 69, 0 ); |
59 | colors[AttributeValue] = QColor( 36, 36, 170 ); |
60 | } |
61 | QHash<int, QColor> colors; |
62 | }; |
63 | |
64 | HtmlHighlighter::HtmlHighlighter( QTextDocument *document ) |
65 | : QSyntaxHighlighter( document ), d( new HtmlHighlighterPrivate ) |
66 | { |
67 | } |
68 | |
69 | HtmlHighlighter::~HtmlHighlighter() |
70 | { |
71 | delete d; |
72 | } |
73 | |
74 | void HtmlHighlighter::highlightBlock( const QString &text ) |
75 | { |
76 | int state = previousBlockState(); |
77 | int len = text.length(); |
78 | int start = 0; |
79 | int pos = 0; |
80 | |
81 | while ( pos < len ) { |
82 | switch ( state ) { |
83 | case HtmlHighlighterPrivate::State_Text: |
84 | default: |
85 | while ( pos < len ) { |
86 | QChar ch = text.at( pos ); |
87 | if ( ch == QLatin1Char( |
88 | if ( text.mid( pos, 4 ) == QLatin1String( "<!--") ) { |
89 | state = HtmlHighlighterPrivate::State_Comment; |
90 | } else { |
91 | if ( text.mid( pos, 9 ).toUpper() == QLatin1String( "<!DOCTYPE") ) { |
92 | state = HtmlHighlighterPrivate::State_DocType; |
93 | } else { |
94 | state = HtmlHighlighterPrivate::State_TagStart; |
95 | } |
96 | } |
97 | break; |
98 | } else if ( ch == QLatin1Char( |
99 | start = pos; |
100 | while ( pos < len && text.at( pos++ ) != QLatin1Char( |
101 | ; |
102 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::Entity] ); |
103 | } else { |
104 | ++pos; |
105 | } |
106 | } |
107 | break; |
108 | |
109 | case HtmlHighlighterPrivate::State_Comment: |
110 | start = pos; |
111 | while ( pos < len ) { |
112 | if ( text.mid( pos, 3 ) == QLatin1String( "-->") ) { |
113 | pos += 3; |
114 | state = HtmlHighlighterPrivate::State_Text; |
115 | break; |
116 | } else { |
117 | ++pos; |
118 | } |
119 | } |
120 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::Comment] ); |
121 | break; |
122 | |
123 | case HtmlHighlighterPrivate::State_DocType: |
124 | start = pos; |
125 | while ( pos < len ) { |
126 | QChar ch = text.at( pos ); |
127 | ++pos; |
128 | if ( ch == QLatin1Char( |
129 | state = HtmlHighlighterPrivate::State_Text; |
130 | break; |
131 | } |
132 | } |
133 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::DocType] ); |
134 | break; |
135 | |
136 | // at '<' in e.g. "<span>foo</span>" |
137 | case HtmlHighlighterPrivate::State_TagStart: |
138 | start = pos + 1; |
139 | while ( pos < len ) { |
140 | QChar ch = text.at( pos ); |
141 | ++pos; |
142 | if ( ch == QLatin1Char( |
143 | state = HtmlHighlighterPrivate::State_Text; |
144 | break; |
145 | } |
146 | if ( !ch.isSpace() ) { |
147 | --pos; |
148 | state = HtmlHighlighterPrivate::State_TagName; |
149 | break; |
150 | } |
151 | } |
152 | break; |
153 | |
154 | // at 'b' in e.g "<blockquote>foo</blockquote>" |
155 | case HtmlHighlighterPrivate::State_TagName: |
156 | start = pos; |
157 | while ( pos < len ) { |
158 | QChar ch = text.at( pos ); |
159 | ++pos; |
160 | if ( ch.isSpace() ) { |
161 | --pos; |
162 | state = HtmlHighlighterPrivate::State_InsideTag; |
163 | break; |
164 | } |
165 | if ( ch == QLatin1Char( |
166 | state = HtmlHighlighterPrivate::State_Text; |
167 | break; |
168 | } |
169 | } |
170 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::Tag] ); |
171 | break; |
172 | |
173 | // anywhere after tag name and before tag closing ('>') |
174 | case HtmlHighlighterPrivate::State_InsideTag: |
175 | start = pos; |
176 | |
177 | while ( pos < len ) { |
178 | QChar ch = text.at( pos ); |
179 | ++pos; |
180 | |
181 | if ( ch == QLatin1Char( |
182 | continue; |
183 | } |
184 | |
185 | if ( ch == QLatin1Char( |
186 | state = HtmlHighlighterPrivate::State_Text; |
187 | break; |
188 | } |
189 | |
190 | if ( !ch.isSpace() ) { |
191 | --pos; |
192 | state = HtmlHighlighterPrivate::State_AttributeName; |
193 | break; |
194 | } |
195 | |
196 | } |
197 | |
198 | break; |
199 | |
200 | // at 's' in e.g. <img src=bla.png/> |
201 | case HtmlHighlighterPrivate::State_AttributeName: |
202 | start = pos; |
203 | |
204 | while ( pos < len ) { |
205 | QChar ch = text.at( pos ); |
206 | ++pos; |
207 | |
208 | if ( ch == QLatin1Char( |
209 | state = HtmlHighlighterPrivate::State_AttributeValue; |
210 | break; |
211 | } |
212 | |
213 | if ( ch == QLatin1Char( |
214 | state = HtmlHighlighterPrivate::State_InsideTag; |
215 | break; |
216 | } |
217 | } |
218 | |
219 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeName] ); |
220 | break; |
221 | |
222 | // after '=' in e.g. <img src=bla.png/> |
223 | case HtmlHighlighterPrivate::State_AttributeValue: |
224 | start = pos; |
225 | |
226 | // find first non-space character |
227 | while ( pos < len ) { |
228 | QChar ch = text.at( pos ); |
229 | ++pos; |
230 | |
231 | // handle opening single quote |
232 | if ( ch == QLatin1Char( |
233 | state = HtmlHighlighterPrivate::State_SingleQuote; |
234 | break; |
235 | } |
236 | |
237 | // handle opening double quote |
238 | if ( ch == QLatin1Char( |
239 | state = HtmlHighlighterPrivate::State_DoubleQuote; |
240 | break; |
241 | } |
242 | |
243 | if ( !ch.isSpace() ) { |
244 | break; |
245 | } |
246 | } |
247 | |
248 | if ( state == HtmlHighlighterPrivate::State_AttributeValue ) { |
249 | // attribute value without quote |
250 | // just stop at non-space or tag delimiter |
251 | start = pos; |
252 | while ( pos < len ) { |
253 | QChar ch = text.at( pos ); |
254 | if ( ch.isSpace() ) { |
255 | break; |
256 | } |
257 | if ( ch == QLatin1Char( |
258 | break; |
259 | } |
260 | ++pos; |
261 | } |
262 | state = HtmlHighlighterPrivate::State_InsideTag; |
263 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeValue] ); |
264 | } |
265 | |
266 | break; |
267 | |
268 | // after the opening single quote in an attribute value |
269 | case HtmlHighlighterPrivate::State_SingleQuote: |
270 | start = pos; |
271 | |
272 | while ( pos < len ) { |
273 | QChar ch = text.at(pos); |
274 | ++pos; |
275 | if ( ch == QLatin1Char( |
276 | break; |
277 | } |
278 | } |
279 | |
280 | state = HtmlHighlighterPrivate::State_InsideTag; |
281 | |
282 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeValue] ); |
283 | break; |
284 | |
285 | // after the opening double quote in an attribute value |
286 | case HtmlHighlighterPrivate::State_DoubleQuote: |
287 | start = pos; |
288 | |
289 | while ( pos < len ) { |
290 | QChar ch = text.at(pos); |
291 | ++pos; |
292 | if ( ch == QLatin1Char( |
293 | break; |
294 | } |
295 | } |
296 | |
297 | state = HtmlHighlighterPrivate::State_InsideTag; |
298 | |
299 | setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeValue] ); |
300 | break; |
301 | |
302 | } |
303 | } |
304 | |
305 | setCurrentBlockState( state ); |
306 | } |
307 | |
308 | } |
309 | |
310 |