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
25namespace KPIMTextEdit {
26
27class 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
64HtmlHighlighter::HtmlHighlighter( QTextDocument *document )
65 : QSyntaxHighlighter( document ), d( new HtmlHighlighterPrivate )
66{
67}
68
69HtmlHighlighter::~HtmlHighlighter()
70{
71 delete d;
72}
73
74void 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( '>' ) || 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( '>' ) || 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