2008-12-02 12:01:29 +01:00
|
|
|
/***************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2009-01-13 19:21:51 +01:00
|
|
|
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** Contact: Qt Software Information (qt-info@nokia.com)
|
|
|
|
|
**
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
|
|
|
|
** Non-Open Source Usage
|
|
|
|
|
**
|
2008-12-02 12:01:29 +01:00
|
|
|
** Licensees may use this file in accordance with the Qt Beta Version
|
|
|
|
|
** License Agreement, Agreement version 2.2 provided with the Software or,
|
|
|
|
|
** alternatively, in accordance with the terms contained in a written
|
2008-12-02 14:17:16 +01:00
|
|
|
** agreement between you and Nokia.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
**
|
2008-12-02 12:01:29 +01:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
|
|
|
** Public License versions 2.0 or 3.0 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the packaging
|
|
|
|
|
** of this file. Please review the following information to ensure GNU
|
|
|
|
|
** General Public Licensing requirements will be met:
|
|
|
|
|
**
|
|
|
|
|
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
|
|
|
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2008-12-02 14:17:16 +01:00
|
|
|
** rights. These rights are described in the Nokia Qt GPL Exception
|
2008-12-16 17:20:00 +01:00
|
|
|
** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
|
|
|
|
***************************************************************************/
|
2008-12-02 15:08:31 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "cpphighlighter.h"
|
|
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
|
2008-12-16 13:19:11 +01:00
|
|
|
CppHighlighter::CppHighlighter(QTextDocument *document) :
|
2008-12-02 12:01:29 +01:00
|
|
|
QSyntaxHighlighter(document)
|
|
|
|
|
{
|
|
|
|
|
visualSpaceFormat.setForeground(Qt::lightGray);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-16 13:19:11 +01:00
|
|
|
void CppHighlighter::highlightBlock(const QString &text)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
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))
|
|
|
|
|
setFormat(tk.position(), tk.length(), m_formats[CppStringFormat]);
|
|
|
|
|
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.is(T_COMMENT)) {
|
|
|
|
|
setFormat(tk.position(), tk.length(), m_formats[CppCommentFormat]);
|
|
|
|
|
// 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]);
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-12-16 13:19:11 +01:00
|
|
|
bool CppHighlighter::isPPKeyword(const QStringRef &text) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-16 13:19:11 +01:00
|
|
|
bool CppHighlighter::isQtKeyword(const QStringRef &text) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-16 13:19:11 +01:00
|
|
|
void CppHighlighter::highlightWord(QStringRef word, int position, int length)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
// 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]);
|
|
|
|
|
}
|
|
|
|
|
}
|