Qt 4.8
qscriptsyntaxhighlighter.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtSCriptTools module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
43 #include "private/qfunctions_p.h"
44 
45 #ifndef QT_NO_SYNTAXHIGHLIGHTER
46 
48 
49 enum ScriptIds {
50  Comment = 1,
57 };
58 
59 #define MAX_KEYWORD 63
60 static 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 
127 {
128  inline KeywordHelper(const QString &word) : needle(word) {}
130 };
131 
132 Q_STATIC_GLOBAL_OPERATOR bool operator<(const KeywordHelper &helper, const char *kw)
133 {
134  return helper.needle < QLatin1String(kw);
135 }
136 
137 Q_STATIC_GLOBAL_OPERATOR bool operator<(const char *kw, const KeywordHelper &helper)
138 {
139  return QLatin1String(kw) < helper.needle;
140 }
141 
142 static 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 
152  : QSyntaxHighlighter(document)
153 {
154 
163 }
164 
166 {
167 }
168 
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 
239  for (;;) {
240  const QChar c = text.at(i);
241 
242  if (lastWasBackSlash) {
243  input = InputSep;
244  } else {
245  switch (c.toAscii()) {
246  case '*':
247  input = InputAsterix;
248  break;
249  case '/':
250  input = InputSlash;
251  break;
252  case '{':
253  braceDepth++;
254  // fall through
255  case '(': case '[':
256  input = InputParen;
257  switch (state) {
258  case StateStandard:
259  case StateNumber:
260  case StatePreProcessor:
261  case StateCCommentEnd2:
262  case StateCCommentEnd1:
263  case StateString2End:
264  case StateStringEnd:
265 // parentheses.push_back(Parenthesis(Parenthesis::Opened, c, i));
266  break;
267  default:
268  break;
269  }
270  break;
271  case '}':
272  if (--braceDepth < 0)
273  braceDepth = 0;
274  // fall through
275  case ')': case ']':
276  input = InputParen;
277  switch (state) {
278  case StateStandard:
279  case StateNumber:
280  case StatePreProcessor:
281  case StateCCommentEnd2:
282  case StateCCommentEnd1:
283  case StateString2End:
284  case StateStringEnd:
285 // parentheses.push_back(Parenthesis(Parenthesis::Closed, c, i));
286  break;
287  default:
288  break;
289  }
290  break;
291  case '#':
292  input = InputHash;
293  break;
294  case '"':
295  input = InputQuotation;
296  break;
297  case '\'':
298  input = InputApostrophe;
299  break;
300  case ' ':
301  input = InputSpace;
302  break;
303  case '1': case '2': case '3': case '4': case '5':
304  case '6': case '7': case '8': case '9': case '0':
305  if (alphabeth.contains(lastChar)
306  && (!mathChars.contains(lastChar) || !numbers.contains(text.at(i - 1)))
307  ) {
308  input = InputAlpha;
309  } else {
310  if (input == InputAlpha && numbers.contains(lastChar))
311  input = InputAlpha;
312  else
313  input = InputNumber;
314  }
315  break;
316  case ':': {
317  input = InputAlpha;
318  const QChar colon = QLatin1Char(':');
319  if (state == StateStandard && !questionMark && lastChar != colon) {
320  const QChar nextChar = i < text.length() - 1 ? text.at(i + 1) : QLatin1Char(' ');
321  if (nextChar != colon)
322  for (int j = 0; j < i; ++j) {
323  if (format(j) == emptyFormat )
325  }
326  }
327  } break;
328  default:
329  if (!questionMark && c == QLatin1Char('?'))
330  questionMark = true;
331  if (c.isLetter() || c == QLatin1Char('_'))
332  input = InputAlpha;
333  else
334  input = InputSep;
335  break;
336  }
337  }
338 
339  if (input != InputSpace && firstNonSpace < 0)
340  firstNonSpace = i;
341 
342  lastWasBackSlash = !lastWasBackSlash && c == QLatin1Char('\\');
343 
344  if (input == InputAlpha)
345  buffer += c;
346 
347  state = table[state][input];
348 
349  switch (state) {
350  case StateStandard: {
351  setFormat(i, 1, emptyFormat);
352  if (makeLastStandard)
353  setFormat(i - 1, 1, emptyFormat);
354  makeLastStandard = false;
355  if (input != InputAlpha) {
356  highlightWord(i, buffer);
357  buffer = QString::null;
358  }
359  } break;
360  case StateCommentStart1:
361  if (makeLastStandard)
362  setFormat(i - 1, 1, emptyFormat);
363  makeLastStandard = true;
364  buffer = QString::null;
365  break;
366  case StateCCommentStart2:
368  makeLastStandard = false;
369 // parentheses.push_back(Parenthesis(Parenthesis::Opened, QLatin1Char('/'), i-1));
370  buffer = QString::null;
371  break;
372  case StateScriptCommentStart2:
373  setFormat(i - 1, 2, m_formats[ScriptCommentFormat]);
374  makeLastStandard = false;
375  buffer = QString::null;
376  break;
377  case StateCComment:
378  if (makeLastStandard)
379  setFormat(i - 1, 1, emptyFormat);
380  makeLastStandard = false;
381  setFormat(i, 1, m_formats[ScriptCommentFormat]);
382  buffer = QString::null;
383  break;
384  case StateScriptComment:
385  if (makeLastStandard)
386  setFormat(i - 1, 1, emptyFormat);
387  makeLastStandard = false;
388  setFormat(i, 1, m_formats[ScriptCommentFormat]);
389  buffer = QString::null;
390  break;
391  case StateCCommentEnd1:
392  if (makeLastStandard)
393  setFormat(i - 1, 1, emptyFormat);
394  makeLastStandard = false;
395  setFormat(i, 1, m_formats[ScriptCommentFormat]);
396  buffer = QString::null;
397  break;
398  case StateCCommentEnd2:
399  if (makeLastStandard)
400  setFormat(i - 1, 1, emptyFormat);
401  makeLastStandard = false;
402  setFormat(i, 1, m_formats[ScriptCommentFormat]);
403 // parentheses.push_back(Parenthesis(Parenthesis::Closed, QLatin1Char('/'), i));
404  buffer = QString::null;
405  break;
406  case StateStringStart:
407  if (makeLastStandard)
408  setFormat(i - 1, 1, emptyFormat);
409  makeLastStandard = false;
410  setFormat(i, 1, emptyFormat);
411  buffer = QString::null;
412  break;
413  case StateString:
414  if (makeLastStandard)
415  setFormat(i - 1, 1, emptyFormat);
416  makeLastStandard = false;
418  buffer = QString::null;
419  break;
420  case StateStringEnd:
421  if (makeLastStandard)
422  setFormat(i - 1, 1, emptyFormat);
423  makeLastStandard = false;
424  setFormat(i, 1, emptyFormat);
425  buffer = QString::null;
426  break;
427  case StateString2Start:
428  if (makeLastStandard)
429  setFormat(i - 1, 1, emptyFormat);
430  makeLastStandard = false;
431  setFormat(i, 1, emptyFormat);
432  buffer = QString::null;
433  break;
434  case StateString2:
435  if (makeLastStandard)
436  setFormat(i - 1, 1, emptyFormat);
437  makeLastStandard = false;
438  setFormat(i, 1, m_formats[ScriptStringFormat]);
439  buffer = QString::null;
440  break;
441  case StateString2End:
442  if (makeLastStandard)
443  setFormat(i - 1, 1, emptyFormat);
444  makeLastStandard = false;
445  setFormat(i, 1, emptyFormat);
446  buffer = QString::null;
447  break;
448  case StateNumber:
449  if (makeLastStandard)
450  setFormat(i - 1, 1, emptyFormat);
451  makeLastStandard = false;
453  buffer = QString::null;
454  break;
455  case StatePreProcessor:
456  if (makeLastStandard)
457  setFormat(i - 1, 1, emptyFormat);
458  makeLastStandard = false;
460  buffer = QString::null;
461  break;
462  }
463 
464  lastChar = c;
465  i++;
466  if (i >= text.length()) {
467 #if 0
468  if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(currentBlock())) {
469  userData->setHasClosingCollapse(false);
470  userData->setCollapseMode(TextBlockUserData::NoCollapse);
471  }
472  int collapse = Parenthesis::collapseAtPos(parentheses);
473  if (collapse >= 0) {
474  if (collapse == firstNonSpace)
475  TextEditDocumentLayout::userData(currentBlock())->setCollapseMode(TextBlockUserData::CollapseThis);
476  else
477  TextEditDocumentLayout::userData(currentBlock())->setCollapseMode(TextBlockUserData::CollapseAfter);
478  }
479  if (Parenthesis::hasClosingCollapse(parentheses)) {
480  TextEditDocumentLayout::userData(currentBlock())->setHasClosingCollapse(true);
481  }
482 #endif
483 
484  break;
485  }
486  }
487 
488  highlightWord(text.length(), buffer);
489 
490  switch (state) {
491  case StateCComment:
492  case StateCCommentEnd1:
493  case StateCCommentStart2:
494  state = StateCComment;
495  break;
496  case StateString:
497  // quotes cannot span multiple lines, so if somebody starts
498  // typing a quoted string we don't need to look for the ending
499  // quote in another line (or highlight until the end of the
500  // document) and therefore slow down editing.
501  state = StateStandard;
502  break;
503  case StateString2:
504  state = StateStandard;
505  break;
506  default:
507  state = StateStandard;
508  break;
509  }
510 
511 #if 0
512  TextEditDocumentLayout::setParentheses(currentBlock(), parentheses);
513 #endif
514 
515  setCurrentBlockState((braceDepth << 8) | state);
516 }
517 
518 void QScriptSyntaxHighlighter::highlightWord(int currentPos, const QString &buffer)
519 {
520  if (buffer.isEmpty())
521  return;
522 
523  // try to highlight Qt 'identifiers' like QObject and buffer.length() >; == QLatin1Char('Q')
524  && (buffer.at(1).isUpper()
525  || buffer.at(1) == QLatin1Char('_')
526  || buffer.at(1) == QLatin1Char('t'))) {
527  setFormat(currentPos - buffer.length(), buffer.length(), m_formats[ScriptTypeFormat]);
528  } else {
529  if (isKeyword(buffer))
530  setFormat(currentPos - buffer.length(), buffer.length(), m_formats[ScriptKeywordFormat]);
531  }
532 }
533 
535 
536 #endif // QT_NO_SYNTAXHIGHLIGHTER
537 
QBool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.h:904
bool isLetter() const
Returns true if the character is a letter (Letter_* categories); otherwise returns false...
Definition: qchar.cpp:653
void setFontItalic(bool italic)
If italic is true, sets the text format&#39;s font to be italic; otherwise the font will be non-italic...
Definition: qtextformat.h:415
The QTextCharFormat class provides formatting information for characters in a QTextDocument.
Definition: qtextformat.h:372
unsigned char c[8]
Definition: qnumeric_p.h:62
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
const QChar at(int i) const
Returns the character at the given index position in the string.
Definition: qstring.h:698
int length() const
Returns the number of characters in this string.
Definition: qstring.h:696
QObjectUserData * userData(uint id) const
Definition: qobject.cpp:4016
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
void setFormat(int start, int count, const QTextCharFormat &format)
This function is applied to the syntax highlighter&#39;s current text block (i.e.
Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
Definition: qalgorithms.h:295
QScriptSyntaxHighlighter(QTextDocument *document=0)
The QString class provides a Unicode character string.
Definition: qstring.h:83
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
int previousBlockState() const
Returns the end state of the text block previous to the syntax highlighter&#39;s current block...
void reserve(int size)
Attempts to allocate memory for at least size characters.
Definition: qstring.h:881
unsigned char uchar
Definition: qglobal.h:994
KeywordHelper(const QString &word)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
#define Q_STATIC_GLOBAL_OPERATOR
Definition: qfunctions_p.h:71
static bool isKeyword(const QString &word)
QTextBlock currentBlock() const
Returns the current text block.
void highlightWord(int currentPos, const QString &buffer)
#define MAX_KEYWORD
bool isUpper() const
Returns true if the character is an uppercase letter, i.
Definition: qchar.h:273
char toAscii() const
Returns the character value of the QChar obtained using the current codec used to read C strings...
Definition: qchar.cpp:1490
void setCurrentBlockState(int newState)
Sets the state of the current text block to newState.
void highlightBlock(const QString &text)
Highlights the given text block.
The QSyntaxHighlighter class allows you to define syntax highlighting rules, and in addition you can ...
static const char *const keywords[MAX_KEYWORD]
Q_STATIC_GLOBAL_OPERATOR bool operator<(const KeywordHelper &helper, const char *kw)
The QTextDocument class holds formatted text that can be viewed and edited using a QTextEdit...
void setForeground(const QBrush &brush)
Sets the foreground brush to the specified brush.
Definition: qtextformat.h:350
QTextCharFormat m_formats[NumScriptFormats]
QTextCharFormat format(int pos) const
Returns the format at position inside the syntax highlighter&#39;s current text block.
static const Null null
Definition: qstring.h:502
static const KeyPair *const end
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
#define text
Definition: qobjectdefs.h:80