source: trunk/src/scripttools/debugging/qscriptsyntaxhighlighter.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 22.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtSCriptTools module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptsyntaxhighlighter_p.h"
43#include "private/qfunctions_p.h"
44
45#ifndef QT_NO_SYNTAXHIGHLIGHTER
46
47QT_BEGIN_NAMESPACE
48
49enum ScriptIds {
50 Comment = 1,
51 Number,
52 String,
53 Type,
54 Keyword,
55 PreProcessor,
56 Label
57};
58
59#define MAX_KEYWORD 63
60static const char *const keywords[MAX_KEYWORD] = {
61 "Infinity",
62 "NaN",
63 "abstract",
64 "boolean",
65 "break",
66 "byte",
67 "case",
68 "catch",
69 "char",
70 "class",
71 "const",
72 "constructor",
73 "continue",
74 "debugger",
75 "default",
76 "delete",
77 "do",
78 "double",
79 "else",
80 "enum",
81 "export",
82 "extends",
83 "false",
84 "final",
85 "finally",
86 "float",
87 "for",
88 "function",
89 "goto",
90 "if",
91 "implements",
92 "import",
93 "in",
94 "instanceof",
95 "int",
96 "interface",
97 "long",
98 "native",
99 "new",
100 "package",
101 "private",
102 "protected",
103 "public",
104 "return",
105 "short",
106 "static",
107 "super",
108 "switch",
109 "synchronized",
110 "this",
111 "throw",
112 "throws",
113 "transient",
114 "true",
115 "try",
116 "typeof",
117 "undefined",
118 "var",
119 "void",
120 "volatile",
121 "while",
122 "with", // end of array
123 0
124};
125
126struct KeywordHelper
127{
128 inline KeywordHelper(const QString &word) : needle(word) {}
129 const QString needle;
130};
131
132Q_STATIC_GLOBAL_OPERATOR bool operator<(const KeywordHelper &helper, const char *kw)
133{
134 return helper.needle < QLatin1String(kw);
135}
136
137Q_STATIC_GLOBAL_OPERATOR bool operator<(const char *kw, const KeywordHelper &helper)
138{
139 return QLatin1String(kw) < helper.needle;
140}
141
142static bool isKeyword(const QString &word)
143{
144 const char * const *start = &keywords[0];
145 const char * const *end = &keywords[MAX_KEYWORD - 1];
146 const char * const *kw = qBinaryFind(start, end, KeywordHelper(word));
147
148 return kw != end;
149}
150
151QScriptSyntaxHighlighter::QScriptSyntaxHighlighter(QTextDocument *document)
152 : QSyntaxHighlighter(document)
153{
154
155 m_formats[ScriptNumberFormat].setForeground(Qt::darkBlue);
156 m_formats[ScriptStringFormat].setForeground(Qt::darkGreen);
157 m_formats[ScriptTypeFormat].setForeground(Qt::darkMagenta);
158 m_formats[ScriptKeywordFormat].setForeground(Qt::darkYellow);
159 m_formats[ScriptPreprocessorFormat].setForeground(Qt::darkBlue);
160 m_formats[ScriptLabelFormat].setForeground(Qt::darkRed);
161 m_formats[ScriptCommentFormat].setForeground(Qt::darkGreen);
162 m_formats[ScriptCommentFormat].setFontItalic(true);
163}
164
165QScriptSyntaxHighlighter::~QScriptSyntaxHighlighter()
166{
167}
168
169void QScriptSyntaxHighlighter::highlightBlock(const QString &text)
170{
171
172 // states
173 enum States { StateStandard, StateCommentStart1, StateCCommentStart2,
174 StateScriptCommentStart2, StateCComment, StateScriptComment, StateCCommentEnd1,
175 StateCCommentEnd2, StateStringStart, StateString, StateStringEnd,
176 StateString2Start, StateString2, StateString2End,
177 StateNumber, StatePreProcessor, NumStates };
178
179 // tokens
180 enum Tokens { InputAlpha, InputNumber, InputAsterix, InputSlash, InputParen,
181 InputSpace, InputHash, InputQuotation, InputApostrophe, InputSep, NumTokens };
182
183 static uchar table[NumStates][NumTokens] = {
184 { StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStandard
185 { StateStandard, StateNumber, StateCCommentStart2, StateScriptCommentStart2, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCommentStart1
186 { StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentStart2
187 { StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment }, // ScriptCommentStart2
188 { StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCComment
189 { StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment, StateScriptComment }, // StateScriptComment
190 { StateCComment, StateCComment, StateCCommentEnd1, StateCCommentEnd2, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentEnd1
191 { StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCCommentEnd2
192 { StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateStringStart
193 { StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateString
194 { StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStringEnd
195 { StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2Start
196 { StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2
197 { StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateString2End
198 { StateNumber, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateNumber
199 { StatePreProcessor, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard } // StatePreProcessor
200 };
201
202 QString buffer;
203 buffer.reserve(text.length());
204
205 QTextCharFormat emptyFormat;
206
207 int state = StateStandard;
208 int braceDepth = 0;
209 const int previousState = previousBlockState();
210 if (previousState != -1) {
211 state = previousState & 0xff;
212 braceDepth = previousState >> 8;
213 }
214
215 if (text.isEmpty()) {
216 setCurrentBlockState(previousState);
217#if 0
218 TextEditDocumentLayout::clearParentheses(currentBlock());
219#endif
220 return;
221 }
222#if 0
223 Parentheses parentheses;
224 parentheses.reserve(20); // assume wizard level ;-)
225#endif
226 int input = -1;
227 int i = 0;
228 bool lastWasBackSlash = false;
229 bool makeLastStandard = false;
230
231 static const QString alphabeth = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
232 static const QString mathChars = QLatin1String("xXeE");
233 static const QString numbers = QLatin1String("0123456789");
234 bool questionMark = false;
235 QChar lastChar;
236
237 int firstNonSpace = -1;
238 int lastNonSpace = -1;
239
240 for (;;) {
241 const QChar c = text.at(i);
242
243 if (lastWasBackSlash) {
244 input = InputSep;
245 } else {
246 switch (c.toAscii()) {
247 case '*':
248 input = InputAsterix;
249 break;
250 case '/':
251 input = InputSlash;
252 break;
253 case '{':
254 braceDepth++;
255 // fall through
256 case '(': case '[':
257 input = InputParen;
258 switch (state) {
259 case StateStandard:
260 case StateNumber:
261 case StatePreProcessor:
262 case StateCCommentEnd2:
263 case StateCCommentEnd1:
264 case StateString2End:
265 case StateStringEnd:
266// parentheses.push_back(Parenthesis(Parenthesis::Opened, c, i));
267 break;
268 default:
269 break;
270 }
271 break;
272 case '}':
273 if (--braceDepth < 0)
274 braceDepth = 0;
275 // fall through
276 case ')': case ']':
277 input = InputParen;
278 switch (state) {
279 case StateStandard:
280 case StateNumber:
281 case StatePreProcessor:
282 case StateCCommentEnd2:
283 case StateCCommentEnd1:
284 case StateString2End:
285 case StateStringEnd:
286// parentheses.push_back(Parenthesis(Parenthesis::Closed, c, i));
287 break;
288 default:
289 break;
290 }
291 break;
292 case '#':
293 input = InputHash;
294 break;
295 case '"':
296 input = InputQuotation;
297 break;
298 case '\'':
299 input = InputApostrophe;
300 break;
301 case ' ':
302 input = InputSpace;
303 break;
304 case '1': case '2': case '3': case '4': case '5':
305 case '6': case '7': case '8': case '9': case '0':
306 if (alphabeth.contains(lastChar)
307 && (!mathChars.contains(lastChar) || !numbers.contains(text.at(i - 1)))
308 ) {
309 input = InputAlpha;
310 } else {
311 if (input == InputAlpha && numbers.contains(lastChar))
312 input = InputAlpha;
313 else
314 input = InputNumber;
315 }
316 break;
317 case ':': {
318 input = InputAlpha;
319 const QChar colon = QLatin1Char(':');
320 if (state == StateStandard && !questionMark && lastChar != colon) {
321 const QChar nextChar = i < text.length() - 1 ? text.at(i + 1) : QLatin1Char(' ');
322 if (nextChar != colon)
323 for (int j = 0; j < i; ++j) {
324 if (format(j) == emptyFormat )
325 setFormat(j, 1, m_formats[ScriptLabelFormat]);
326 }
327 }
328 } break;
329 default:
330 if (!questionMark && c == QLatin1Char('?'))
331 questionMark = true;
332 if (c.isLetter() || c == QLatin1Char('_'))
333 input = InputAlpha;
334 else
335 input = InputSep;
336 break;
337 }
338 }
339
340 if (input != InputSpace) {
341 if (firstNonSpace < 0)
342 firstNonSpace = i;
343 lastNonSpace = i;
344 }
345
346 lastWasBackSlash = !lastWasBackSlash && c == QLatin1Char('\\');
347
348 if (input == InputAlpha)
349 buffer += c;
350
351 state = table[state][input];
352
353 switch (state) {
354 case StateStandard: {
355 setFormat(i, 1, emptyFormat);
356 if (makeLastStandard)
357 setFormat(i - 1, 1, emptyFormat);
358 makeLastStandard = false;
359 if (input != InputAlpha) {
360 highlightWord(i, buffer);
361 buffer = QString::null;
362 }
363 } break;
364 case StateCommentStart1:
365 if (makeLastStandard)
366 setFormat(i - 1, 1, emptyFormat);
367 makeLastStandard = true;
368 buffer = QString::null;
369 break;
370 case StateCCommentStart2:
371 setFormat(i - 1, 2, m_formats[ScriptCommentFormat]);
372 makeLastStandard = false;
373// parentheses.push_back(Parenthesis(Parenthesis::Opened, QLatin1Char('/'), i-1));
374 buffer = QString::null;
375 break;
376 case StateScriptCommentStart2:
377 setFormat(i - 1, 2, m_formats[ScriptCommentFormat]);
378 makeLastStandard = false;
379 buffer = QString::null;
380 break;
381 case StateCComment:
382 if (makeLastStandard)
383 setFormat(i - 1, 1, emptyFormat);
384 makeLastStandard = false;
385 setFormat(i, 1, m_formats[ScriptCommentFormat]);
386 buffer = QString::null;
387 break;
388 case StateScriptComment:
389 if (makeLastStandard)
390 setFormat(i - 1, 1, emptyFormat);
391 makeLastStandard = false;
392 setFormat(i, 1, m_formats[ScriptCommentFormat]);
393 buffer = QString::null;
394 break;
395 case StateCCommentEnd1:
396 if (makeLastStandard)
397 setFormat(i - 1, 1, emptyFormat);
398 makeLastStandard = false;
399 setFormat(i, 1, m_formats[ScriptCommentFormat]);
400 buffer = QString::null;
401 break;
402 case StateCCommentEnd2:
403 if (makeLastStandard)
404 setFormat(i - 1, 1, emptyFormat);
405 makeLastStandard = false;
406 setFormat(i, 1, m_formats[ScriptCommentFormat]);
407// parentheses.push_back(Parenthesis(Parenthesis::Closed, QLatin1Char('/'), i));
408 buffer = QString::null;
409 break;
410 case StateStringStart:
411 if (makeLastStandard)
412 setFormat(i - 1, 1, emptyFormat);
413 makeLastStandard = false;
414 setFormat(i, 1, emptyFormat);
415 buffer = QString::null;
416 break;
417 case StateString:
418 if (makeLastStandard)
419 setFormat(i - 1, 1, emptyFormat);
420 makeLastStandard = false;
421 setFormat(i, 1, m_formats[ScriptStringFormat]);
422 buffer = QString::null;
423 break;
424 case StateStringEnd:
425 if (makeLastStandard)
426 setFormat(i - 1, 1, emptyFormat);
427 makeLastStandard = false;
428 setFormat(i, 1, emptyFormat);
429 buffer = QString::null;
430 break;
431 case StateString2Start:
432 if (makeLastStandard)
433 setFormat(i - 1, 1, emptyFormat);
434 makeLastStandard = false;
435 setFormat(i, 1, emptyFormat);
436 buffer = QString::null;
437 break;
438 case StateString2:
439 if (makeLastStandard)
440 setFormat(i - 1, 1, emptyFormat);
441 makeLastStandard = false;
442 setFormat(i, 1, m_formats[ScriptStringFormat]);
443 buffer = QString::null;
444 break;
445 case StateString2End:
446 if (makeLastStandard)
447 setFormat(i - 1, 1, emptyFormat);
448 makeLastStandard = false;
449 setFormat(i, 1, emptyFormat);
450 buffer = QString::null;
451 break;
452 case StateNumber:
453 if (makeLastStandard)
454 setFormat(i - 1, 1, emptyFormat);
455 makeLastStandard = false;
456 setFormat(i, 1, m_formats[ScriptNumberFormat]);
457 buffer = QString::null;
458 break;
459 case StatePreProcessor:
460 if (makeLastStandard)
461 setFormat(i - 1, 1, emptyFormat);
462 makeLastStandard = false;
463 setFormat(i, 1, m_formats[ScriptPreprocessorFormat]);
464 buffer = QString::null;
465 break;
466 }
467
468 lastChar = c;
469 i++;
470 if (i >= text.length()) {
471#if 0
472 if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(currentBlock())) {
473 userData->setHasClosingCollapse(false);
474 userData->setCollapseMode(TextBlockUserData::NoCollapse);
475 }
476 int collapse = Parenthesis::collapseAtPos(parentheses);
477 if (collapse >= 0) {
478 if (collapse == firstNonSpace)
479 TextEditDocumentLayout::userData(currentBlock())->setCollapseMode(TextBlockUserData::CollapseThis);
480 else
481 TextEditDocumentLayout::userData(currentBlock())->setCollapseMode(TextBlockUserData::CollapseAfter);
482 }
483 if (Parenthesis::hasClosingCollapse(parentheses)) {
484 TextEditDocumentLayout::userData(currentBlock())->setHasClosingCollapse(true);
485 }
486#endif
487
488 break;
489 }
490 }
491
492 highlightWord(text.length(), buffer);
493
494 switch (state) {
495 case StateCComment:
496 case StateCCommentEnd1:
497 case StateCCommentStart2:
498 state = StateCComment;
499 break;
500 case StateString:
501 // quotes cannot span multiple lines, so if somebody starts
502 // typing a quoted string we don't need to look for the ending
503 // quote in another line (or highlight until the end of the
504 // document) and therefore slow down editing.
505 state = StateStandard;
506 break;
507 case StateString2:
508 state = StateStandard;
509 break;
510 default:
511 state = StateStandard;
512 break;
513 }
514
515#if 0
516 TextEditDocumentLayout::setParentheses(currentBlock(), parentheses);
517#endif
518
519 setCurrentBlockState((braceDepth << 8) | state);
520}
521
522void QScriptSyntaxHighlighter::highlightWord(int currentPos, const QString &buffer)
523{
524 if (buffer.isEmpty())
525 return;
526
527 // try to highlight Qt 'identifiers' like QObject and Q_PROPERTY
528 // but don't highlight words like 'Query'
529 if (buffer.length() > 1
530 && buffer.at(0) == QLatin1Char('Q')
531 && (buffer.at(1).isUpper()
532 || buffer.at(1) == QLatin1Char('_')
533 || buffer.at(1) == QLatin1Char('t'))) {
534 setFormat(currentPos - buffer.length(), buffer.length(), m_formats[ScriptTypeFormat]);
535 } else {
536 if (isKeyword(buffer))
537 setFormat(currentPos - buffer.length(), buffer.length(), m_formats[ScriptKeywordFormat]);
538 }
539}
540
541QT_END_NAMESPACE
542
543#endif // QT_NO_SYNTAXHIGHLIGHTER
544
Note: See TracBrowser for help on using the repository browser.