Newer
Older
/**************************************************************************
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**************************************************************************/
#include <Token.h>
#include <cplusplus/SimpleLexer.h>
#include <texteditor/basetexteditor.h>
#include <QtGui/QTextDocument>
#include <QtCore/QDebug>
using namespace CppEditor::Internal;
using namespace TextEditor;
using namespace CPlusPlus;
CppHighlighter::CppHighlighter(QTextDocument *document) :
QSyntaxHighlighter(document)
{
visualSpaceFormat.setForeground(Qt::lightGray);
}
void CppHighlighter::highlightBlock(const QString &text)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{
QTextCharFormat emptyFormat;
const int previousState = previousBlockState();
int state = 0, braceDepth = 0;
if (previousState != -1) {
state = previousState & 0xff;
braceDepth = previousState >> 8;
}
SimpleLexer tokenize;
tokenize.setQtMocRunEnabled(false);
int initialState = state;
const QList<SimpleToken> tokens = tokenize(text, initialState);
state = tokenize.state(); // refresh the state
if (tokens.isEmpty()) {
setCurrentBlockState(previousState);
if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(currentBlock())) {
userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse);
userData->setCollapseMode(TextBlockUserData::NoCollapse);
}
TextEditDocumentLayout::clearParentheses(currentBlock());
return;
}
const int firstNonSpace = tokens.first().position();
Parentheses parentheses;
parentheses.reserve(20); // assume wizard level ;-)
bool highlightAsPreprocessor = false;
for (int i = 0; i < tokens.size(); ++i) {
const SimpleToken &tk = tokens.at(i);
int previousTokenEnd = 0;
if (i != 0) {
// mark the whitespaces
previousTokenEnd = tokens.at(i - 1).position() +
tokens.at(i - 1).length();
}
if (previousTokenEnd != tk.position()) {
setFormat(previousTokenEnd, tk.position() - previousTokenEnd,
visualSpaceFormat);
}
if (tk.is(T_LPAREN) || tk.is(T_LBRACE) || tk.is(T_LBRACKET)) {
const QChar c(tk.text().at(0));
parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.position()));
if (tk.is(T_LBRACE))
++braceDepth;
} else if (tk.is(T_RPAREN) || tk.is(T_RBRACE) || tk.is(T_RBRACKET)) {
const QChar c(tk.text().at(0));
parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.position()));
if (tk.is(T_RBRACE)) {
if (--braceDepth < 0)
braceDepth = 0;
}
}
bool highlightCurrentWordAsPreprocessor = highlightAsPreprocessor;
if (highlightAsPreprocessor)
highlightAsPreprocessor = false;
if (i == 0 && tk.is(T_POUND)) {
setFormat(tk.position(), tk.length(), m_formats[CppPreprocessorFormat]);
highlightAsPreprocessor = true;
} else if (highlightCurrentWordAsPreprocessor &&
(tk.isKeyword() || tk.is(T_IDENTIFIER)) && isPPKeyword(tk.text()))
setFormat(tk.position(), tk.length(), m_formats[CppPreprocessorFormat]);
else if (tk.is(T_INT_LITERAL) || tk.is(T_FLOAT_LITERAL))
setFormat(tk.position(), tk.length(), m_formats[CppNumberFormat]);
else if (tk.is(T_STRING_LITERAL) || tk.is(T_CHAR_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL) ||
tk.is(T_AT_STRING_LITERAL))
else if (tk.is(T_WIDE_STRING_LITERAL) || tk.is(T_WIDE_CHAR_LITERAL))
setFormat(tk.position(), tk.length(), m_formats[CppStringFormat]);
else if (tk.isComment()) {
if (tk.is(T_COMMENT))
setFormat(tk.position(), tk.length(), m_formats[CppCommentFormat]);
else // a doxygen comment
highlightDoxygenComment(text, tk.position(), tk.length());
// we need to insert a close comment parenthesis, if
// - the line starts in a C Comment (initalState != 0)
// - the first token of the line is a T_COMMENT (i == 0 && tk.is(T_COMMENT))
// - is not a continuation line (tokens.size() > 1 || ! state)
if (initialState && i == 0 && (tokens.size() > 1 || ! state)) {
--braceDepth;
const int tokenEnd = tk.position() + tk.length() - 1;
parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd));
// clear the initial state.
initialState = 0;
}
} else if (tk.isKeyword() || isQtKeyword(tk.text()))
setFormat(tk.position(), tk.length(), m_formats[CppKeywordFormat]);
else if (tk.isOperator())
setFormat(tk.position(), tk.length(), m_formats[CppOperatorFormat]);
else if (i == 0 && tokens.size() > 1 && tk.is(T_IDENTIFIER) && tokens.at(1).is(T_COLON))
setFormat(tk.position(), tk.length(), m_formats[CppLabelFormat]);
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
else if (tk.is(T_IDENTIFIER))
highlightWord(tk.text(), tk.position(), tk.length());
}
// mark the trailing white spaces
if (! tokens.isEmpty()) {
const SimpleToken tk = tokens.last();
const int lastTokenEnd = tk.position() + tk.length();
if (text.length() > lastTokenEnd)
setFormat(lastTokenEnd, text.length() - lastTokenEnd, visualSpaceFormat);
}
if (TextBlockUserData *userData = TextEditDocumentLayout::testUserData(currentBlock())) {
userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse);
userData->setCollapseMode(TextBlockUserData::NoCollapse);
}
if (! initialState && state && ! tokens.isEmpty()) {
parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'),
tokens.last().position()));
++braceDepth;
}
QChar c;
int collapse = Parenthesis::collapseAtPos(parentheses, &c);
if (collapse >= 0) {
TextBlockUserData::CollapseMode collapseMode = TextBlockUserData::CollapseAfter;
if (collapse == firstNonSpace && c != QLatin1Char('+'))
collapseMode = TextBlockUserData::CollapseThis;
TextEditDocumentLayout::userData(currentBlock())->setCollapseMode(collapseMode);
}
int cc = Parenthesis::closeCollapseAtPos(parentheses);
if (cc >= 0) {
TextBlockUserData *userData = TextEditDocumentLayout::userData(currentBlock());
userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapse);
QString trailingText = text.mid(cc+1).simplified();
if (trailingText.isEmpty() || trailingText == QLatin1String(";")) {
userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapseAtEnd);
}
}
TextEditDocumentLayout::setParentheses(currentBlock(), parentheses);
setCurrentBlockState((braceDepth << 8) | tokenize.state());
}
bool CppHighlighter::isPPKeyword(const QStringRef &text) const
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
{
switch (text.length())
{
case 2:
if (text.at(0) == 'i' && text.at(1) == 'f')
return true;
break;
case 4:
if (text.at(0) == 'e' && text == QLatin1String("elif"))
return true;
else if (text.at(0) == 'e' && text == QLatin1String("else"))
return true;
break;
case 5:
if (text.at(0) == 'i' && text == QLatin1String("ifdef"))
return true;
else if (text.at(0) == 'u' && text == QLatin1String("undef"))
return true;
else if (text.at(0) == 'e' && text == QLatin1String("endif"))
return true;
else if (text.at(0) == 'e' && text == QLatin1String("error"))
return true;
break;
case 6:
if (text.at(0) == 'i' && text == QLatin1String("ifndef"))
return true;
if (text.at(0) == 'i' && text == QLatin1String("import"))
return true;
else if (text.at(0) == 'd' && text == QLatin1String("define"))
return true;
else if (text.at(0) == 'p' && text == QLatin1String("pragma"))
return true;
break;
case 7:
if (text.at(0) == 'i' && text == QLatin1String("include"))
return true;
else if (text.at(0) == 'w' && text == QLatin1String("warning"))
return true;
break;
case 12:
if (text.at(0) == 'i' && text == QLatin1String("include_next"))
return true;
break;
default:
break;
}
return false;
}
bool CppHighlighter::isQtKeyword(const QStringRef &text) const
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
{
switch (text.length()) {
case 4:
if (text.at(0) == 'e' && text == QLatin1String("emit"))
return true;
else if (text.at(0) == 'S' && text == QLatin1String("SLOT"))
return true;
break;
case 5:
if (text.at(0) == 's' && text == QLatin1String("slots"))
return true;
break;
case 6:
if (text.at(0) == 'S' && text == QLatin1String("SIGNAL"))
return true;
break;
case 7:
if (text.at(0) == 's' && text == QLatin1String("signals"))
return true;
else if (text.at(0) == 'f' && text == QLatin1String("foreach"))
return true;
else if (text.at(0) == 'f' && text == QLatin1String("forever"))
return true;
break;
default:
break;
}
return false;
}
void CppHighlighter::highlightWord(QStringRef word, int position, int length)
{
// try to highlight Qt 'identifiers' like QObject and Q_PROPERTY
// but don't highlight words like 'Query'
if (word.length() > 1
&& word.at(0) == QLatin1Char('Q')
&& (word.at(1).isUpper()
|| word.at(1) == QLatin1Char('_')
|| word.at(1) == QLatin1Char('t'))) {
setFormat(position, length, m_formats[CppTypeFormat]);
}
}
void CppHighlighter::highlightDoxygenComment(const QString &text, int position, int)
{
int initial = position;
const QChar *uc = text.unicode();
const QChar *it = uc + position;
const QTextCharFormat &format = m_formats[CppDoxygenCommentFormat];
const QTextCharFormat &kwFormat = m_formats[CppDoxygenTagFormat];
while (! it->isNull()) {
if (it->unicode() == QLatin1Char('\\') ||
it->unicode() == QLatin1Char('@')) {
++it;
const QChar *start = it;
while (it->isLetterOrNumber() || it->unicode() == '_')
++it;
int k = CppTools::classifyDoxygenTag(start, it - start);
if (k != CppTools::T_DOXY_IDENTIFIER) {