diff options
author | con <qtc-commiter@nokia.com> | 2008-12-02 12:01:29 +0100 |
---|---|---|
committer | con <qtc-commiter@nokia.com> | 2008-12-02 12:01:29 +0100 |
commit | 05c35356abc31549c5db6eba31fb608c0365c2a0 (patch) | |
tree | be044530104267afaff13f8943889cb97f8c8bad /src/plugins/cpptools | |
download | qt-creator-05c35356abc31549c5db6eba31fb608c0365c2a0.tar.gz |
Initial import
Diffstat (limited to 'src/plugins/cpptools')
36 files changed, 6762 insertions, 0 deletions
diff --git a/src/plugins/cpptools/CppTools.pluginspec b/src/plugins/cpptools/CppTools.pluginspec new file mode 100644 index 0000000000..ca4a8a0e52 --- /dev/null +++ b/src/plugins/cpptools/CppTools.pluginspec @@ -0,0 +1,12 @@ +<plugin name="CppTools" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Tools for analyzing C/C++ code.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="QuickOpen" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/cpptools/cppcodecompletion.cpp b/src/plugins/cpptools/cppcodecompletion.cpp new file mode 100644 index 0000000000..5950477184 --- /dev/null +++ b/src/plugins/cpptools/cppcodecompletion.cpp @@ -0,0 +1,1040 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppcodecompletion.h" +#include "cppmodelmanager.h" + +#include <Control.h> +#include <AST.h> +#include <ASTVisitor.h> +#include <CoreTypes.h> +#include <Literals.h> +#include <Names.h> +#include <NameVisitor.h> +#include <Symbols.h> +#include <SymbolVisitor.h> +#include <Scope.h> +#include <TranslationUnit.h> +#include <cplusplus/ResolveExpression.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Overview.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/TokenUnderCursor.h> + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/itexteditor.h> +#include <texteditor/itexteditable.h> +#include <texteditor/basetexteditor.h> + +#include <QtCore/QDebug> +#include <QtCore/QMap> +#include <QtCore/QFile> +#include <QtGui/QAction> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QVBoxLayout> +#include <QtGui/QApplication> + +using namespace CPlusPlus; + +namespace CppTools { +namespace Internal { + +class FunctionArgumentWidget : public QLabel { +public: + FunctionArgumentWidget(Core::ICore *core); + void showFunctionHint(Function *functionSymbol); + +protected: + bool eventFilter(QObject *obj, QEvent *e); + +private: + void update(); + void close(); + void updateHintText(); + + int m_startpos; + int m_currentarg; + + TextEditor::ITextEditor *m_editor; + + QFrame *m_popupFrame; + Function *m_item; +}; + +class ConvertToCompletionItem: protected NameVisitor +{ + // The completion collector. + CppCodeCompletion *_collector; + + // The completion item. + TextEditor::CompletionItem _item; + + // The current symbol. + Symbol *_symbol; + + // The pretty printer. + Overview overview; + +public: + ConvertToCompletionItem(CppCodeCompletion *collector) + : _collector(collector), + _item(0), + _symbol(0) + { } + + TextEditor::CompletionItem operator()(Symbol *symbol) + { + if (! symbol || ! symbol->name() || symbol->name()->isQualifiedNameId()) + return 0; + + TextEditor::CompletionItem previousItem = switchCompletionItem(0); + Symbol *previousSymbol = switchSymbol(symbol); + accept(symbol->identity()); + if (_item) + _item.m_data = QVariant::fromValue(symbol); + (void) switchSymbol(previousSymbol); + return switchCompletionItem(previousItem); + } + +protected: + Symbol *switchSymbol(Symbol *symbol) + { + Symbol *previousSymbol = _symbol; + _symbol = symbol; + return previousSymbol; + } + + TextEditor::CompletionItem switchCompletionItem(TextEditor::CompletionItem item) + { + TextEditor::CompletionItem previousItem = _item; + _item = item; + return previousItem; + } + + TextEditor::CompletionItem newCompletionItem(Name *name) + { + TextEditor::CompletionItem item(_collector); + item.m_text = overview.prettyName(name); + item.m_icon = _collector->iconForSymbol(_symbol); + return item; + } + + virtual void visit(NameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(TemplateNameId *name) + { + _item = newCompletionItem(name); + _item.m_text = QLatin1String(name->identifier()->chars()); + } + + virtual void visit(DestructorNameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(OperatorNameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(ConversionNameId *name) + { _item = newCompletionItem(name); } + + virtual void visit(QualifiedNameId *name) + { _item = newCompletionItem(name->unqualifiedNameId()); } +}; + + +} // namespace Internal +} // namespace CppTools + + + +using namespace CppTools::Internal; + +FunctionArgumentWidget::FunctionArgumentWidget(Core::ICore *core) + : m_item(0) +{ + QObject *editorObject = core->editorManager()->currentEditor(); + m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject); + + m_popupFrame = new QFrame(0, Qt::ToolTip|Qt::WindowStaysOnTopHint); + m_popupFrame->setFocusPolicy(Qt::NoFocus); + m_popupFrame->setAttribute(Qt::WA_DeleteOnClose); + + setFrameStyle(QFrame::Box); + setFrameShadow(QFrame::Plain); + + setParent(m_popupFrame); + setFocusPolicy(Qt::NoFocus); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(this); + layout->setMargin(0); + m_popupFrame->setLayout(layout); + + QPalette pal = palette(); + setAutoFillBackground(true); + pal.setColor(QPalette::Background, QColor(255, 255, 220)); + setPalette(pal); + + setTextFormat(Qt::RichText); + setMargin(1); +} + +void FunctionArgumentWidget::showFunctionHint(Function *functionSymbol) +{ + m_item = functionSymbol; + m_startpos = m_editor->position(); + + // update the text + m_currentarg = -1; + update(); + + QPoint pos = m_editor->cursorRect().topLeft(); + pos.setY(pos.y() - sizeHint().height()); + m_popupFrame->move(pos); + m_popupFrame->show(); + + QCoreApplication::instance()->installEventFilter(this); +} + +void FunctionArgumentWidget::update() +{ + int curpos = m_editor->position(); + if (curpos < m_startpos) { + close(); + return; + } + + QString str = m_editor->textAt(m_startpos, curpos - m_startpos); + int argnr = 0; + int parcount = 0; + SimpleLexer tokenize; + QList<SimpleToken> tokens = tokenize(str); + for (int i = 0; i < tokens.count(); ++i) { + const SimpleToken &tk = tokens.at(i); + if (tk.is(T_LPAREN)) + ++parcount; + else if (tk.is(T_RPAREN)) + --parcount; + else if (! parcount && tk.is(T_COMMA)) + ++argnr; + } + + if (m_currentarg != argnr) { + m_currentarg = argnr; + updateHintText(); + } + + if (parcount < 0) + close(); +} + +bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e) +{ + switch (e->type()) { + case QEvent::KeyRelease: + { + if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) { + close(); + return false; + } + update(); + break; + } + case QEvent::WindowDeactivate: + case QEvent::Leave: + case QEvent::FocusOut: + { + if (obj != m_editor->widget()) + break; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::Wheel: + close(); + break; + default: + break; + } + + return false; +} + +void FunctionArgumentWidget::close() +{ + m_popupFrame->close(); +} + +void FunctionArgumentWidget::updateHintText() +{ + Overview overview; + overview.setShowReturnTypes(true); + overview.setShowArgumentNames(true); + overview.setMarkArgument(m_currentarg + 1); + QString text = overview(m_item->type(), m_item->name()); + setText(text); +} + +CppCodeCompletion::CppCodeCompletion(CppModelManager *manager, Core::ICore *core) + : ICompletionCollector(manager), + m_core(core), + m_manager(manager), + m_forcedCompletion(false), + m_completionOperator(T_EOF_SYMBOL) +{ } + +QIcon CppCodeCompletion::iconForSymbol(Symbol *symbol) const +{ return m_icons.iconForSymbol(symbol); } + +/* + Searches beckward for an access operator. +*/ +static int startOfOperator(TextEditor::ITextEditable *editor, + int pos, unsigned *kind, + bool wantFunctionCall) +{ + const QChar ch = pos > -1 ? editor->characterAt(pos - 1) : QChar(); + const QChar ch2 = pos > 0 ? editor->characterAt(pos - 2) : QChar(); + const QChar ch3 = pos > 1 ? editor->characterAt(pos - 3) : QChar(); + + int start = pos; + + if (ch2 != QLatin1Char('.') && ch == QLatin1Char('.')) { + if (kind) + *kind = T_DOT; + --start; + } else if (wantFunctionCall && ch == QLatin1Char('(')) { + if (kind) + *kind = T_LPAREN; + --start; + } else if (ch2 == QLatin1Char(':') && ch == QLatin1Char(':')) { + if (kind) + *kind = T_COLON_COLON; + start -= 2; + } else if (ch2 == QLatin1Char('-') && ch == QLatin1Char('>')) { + if (kind) + *kind = T_ARROW; + start -= 2; + } else if (ch2 == QLatin1Char('.') && ch == QLatin1Char('*')) { + if (kind) + *kind = T_DOT_STAR; + start -= 2; + } else if (ch3 == QLatin1Char('-') && ch2 == QLatin1Char('>') && ch == QLatin1Char('*')) { + if (kind) + *kind = T_ARROW_STAR; + start -= 3; + } + + if (start != pos) { + TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget()); + QTextCursor tc(edit->textCursor()); + tc.setPosition(pos); + static CPlusPlus::TokenUnderCursor tokenUnderCursor; + const SimpleToken tk = tokenUnderCursor(tc); + if (tk.is(T_COMMENT) || tk.isLiteral()) { + if (kind) + *kind = T_EOF_SYMBOL; + return pos; + } + } + + return start; +} + +bool CppCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor) +{ + if (! m_manager->isCppEditor(editor)) // ### remove me + return false; + + const int pos = editor->position(); + if (startOfOperator(editor, pos, /*token =*/ 0, + /*want function call=*/ true) != pos) + return true; + + return false; +} + +int CppCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) +{ + TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget()); + if (! edit) + return -1; + + m_editor = editor; + m_startPosition = findStartOfName(editor); + m_completionOperator = T_EOF_SYMBOL; + + int endOfExpression = m_startPosition; + + // Skip whitespace preceding this position + while (editor->characterAt(endOfExpression - 1).isSpace()) + --endOfExpression; + + endOfExpression = startOfOperator(editor, endOfExpression, + &m_completionOperator, + /*want function call =*/ editor->position() == endOfExpression); + + Core::IFile *file = editor->file(); + QString fileName = file->fileName(); + + int line = 0, column = 0; + edit->convertPosition(editor->position(), &line, &column); + // qDebug() << "line:" << line << "column:" << column; + + ExpressionUnderCursor expressionUnderCursor; + QString expression; + + if (m_completionOperator) { + QTextCursor tc(edit->document()); + tc.setPosition(endOfExpression); + expression = expressionUnderCursor(tc); + if (m_completionOperator == T_LPAREN) { + if (expression.endsWith(QLatin1String("SIGNAL"))) + m_completionOperator = T_SIGNAL; + else if (expression.endsWith(QLatin1String("SLOT"))) + m_completionOperator = T_SLOT; + } + } + + //if (! expression.isEmpty()) + //qDebug() << "***** expression:" << expression; + + if (Document::Ptr thisDocument = m_manager->document(fileName)) { + Symbol *symbol = thisDocument->findSymbolAt(line, column); + + typeOfExpression.setDocuments(m_manager->documents()); + + QList<TypeOfExpression::Result> resolvedTypes = typeOfExpression(expression, thisDocument, symbol); + LookupContext context = typeOfExpression.lookupContext(); + + if (!typeOfExpression.expressionAST() && (! m_completionOperator || + m_completionOperator == T_COLON_COLON)) { + if (!m_completionOperator) { + addKeywords(); + addMacros(context); + } + + const QList<Scope *> scopes = context.expand(context.visibleScopes()); + foreach (Scope *scope, scopes) { + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + addCompletionItem(scope->symbolAt(i)); + } + } + return m_startPosition; + } + + // qDebug() << "found" << resolvedTypes.count() << "symbols for expression:" << expression; + + if (resolvedTypes.isEmpty() && (m_completionOperator == T_SIGNAL || + m_completionOperator == T_SLOT)) { + // Apply signal/slot completion on 'this' + expression = QLatin1String("this"); + resolvedTypes = typeOfExpression(expression, thisDocument, symbol); + context = typeOfExpression.lookupContext(); + } + + if (! resolvedTypes.isEmpty()) { + FullySpecifiedType exprTy = resolvedTypes.first().first; + + if (exprTy->isReferenceType()) + exprTy = exprTy->asReferenceType()->elementType(); + + if (m_completionOperator == T_LPAREN && completeFunction(exprTy, resolvedTypes, context)) { + return m_startPosition; + } if ((m_completionOperator == T_DOT || m_completionOperator == T_ARROW) && + completeMember(exprTy, resolvedTypes, context)) { + return m_startPosition; + } else if (m_completionOperator == T_COLON_COLON && completeScope(exprTy, resolvedTypes, context)) { + return m_startPosition; + } else if (m_completionOperator == T_SIGNAL && completeSignal(exprTy, resolvedTypes, context)) { + return m_startPosition; + } else if (m_completionOperator == T_SLOT && completeSlot(exprTy, resolvedTypes, context)) { + return m_startPosition; + } + } + } + + // nothing to do. + return -1; +} + +bool CppCodeCompletion::completeFunction(FullySpecifiedType exprTy, + const QList<TypeOfExpression::Result> &resolvedTypes, + const LookupContext &) +{ + ConvertToCompletionItem toCompletionItem(this); + Overview o; + o.setShowReturnTypes(true); + o.setShowArgumentNames(true); + + if (Class *klass = exprTy->asClass()) { + for (unsigned i = 0; i < klass->memberCount(); ++i) { + Symbol *member = klass->memberAt(i); + if (! member->type()->isFunction()) + continue; + else if (! member->identity()) + continue; + else if (! member->identity()->isEqualTo(klass->identity())) + continue; + if (TextEditor::CompletionItem item = toCompletionItem(member)) { + item.m_text = o(member->type(), member->name()); + m_completions.append(item); + } + } + } else { + QSet<QString> signatures; + foreach (TypeOfExpression::Result p, resolvedTypes) { + FullySpecifiedType ty = p.first; + if (Function *fun = ty->asFunction()) { + if (TextEditor::CompletionItem item = toCompletionItem(fun)) { + QString signature; + signature += overview.prettyName(fun->name()); + signature += overview.prettyType(fun->type()); + if (signatures.contains(signature)) + continue; + signatures.insert(signature); + + item.m_text = o(ty, fun->name()); + m_completions.append(item); + } + } + } + } + + return ! m_completions.isEmpty(); +} + +bool CppCodeCompletion::completeMember(FullySpecifiedType, + const QList<TypeOfExpression::Result> &results, + const LookupContext &context) +{ + Q_ASSERT(! results.isEmpty()); + + QList<Symbol *> classObjectCandidates; + + TypeOfExpression::Result p = results.first(); + + if (m_completionOperator == T_ARROW) { + FullySpecifiedType ty = p.first; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (NamedType *namedTy = ty->asNamedType()) { + ResolveExpression resolveExpression(context); + + Name *className = namedTy->name(); + const QList<Symbol *> candidates = + context.resolveClass(className, context.visibleScopes(p)); + + foreach (Symbol *classObject, candidates) { + const QList<TypeOfExpression::Result> overloads = + resolveExpression.resolveArrowOperator(p, namedTy, + classObject->asClass()); + + foreach (TypeOfExpression::Result r, overloads) { + FullySpecifiedType ty = r.first; + Function *funTy = ty->asFunction(); + if (! funTy) + continue; + + ty = funTy->returnType(); + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + if (PointerType *ptrTy = ty->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { + const QList<Symbol *> classes = + context.resolveClass(namedTy->name(), + context.visibleScopes(p)); + + foreach (Symbol *c, classes) { + if (! classObjectCandidates.contains(c)) + classObjectCandidates.append(c); + } + } + } + } + } + } else if (PointerType *ptrTy = ty->asPointerType()) { + if (NamedType *namedTy = ptrTy->elementType()->asNamedType()) { + const QList<Symbol *> classes = + context.resolveClass(namedTy->name(), + context.visibleScopes(p)); + + foreach (Symbol *c, classes) { + if (! classObjectCandidates.contains(c)) + classObjectCandidates.append(c); + } + } + } + } else if (m_completionOperator == T_DOT) { + FullySpecifiedType ty = p.first; + + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + + NamedType *namedTy = 0; + if (PointerType *ptrTy = ty->asPointerType()) { + // Replace . with -> + int length = m_editor->position() - m_startPosition + 1; + m_editor->setCurPos(m_startPosition - 1); + m_editor->replace(length, QLatin1String("->")); + m_startPosition++; + namedTy = ptrTy->elementType()->asNamedType(); + } else { + namedTy = ty->asNamedType(); + if (! namedTy) { + Function *fun = ty->asFunction(); + if (fun && (fun->scope()->isBlockScope() || fun->scope()->isNamespaceScope())) + namedTy = fun->returnType()->asNamedType(); + } + } + + if (namedTy) { + const QList<Symbol *> classes = + context.resolveClass(namedTy->name(), + context.visibleScopes(p)); + + foreach (Symbol *c, classes) { + if (! classObjectCandidates.contains(c)) + classObjectCandidates.append(c); + } + } + } + + completeClass(classObjectCandidates, context, /*static lookup = */ false); + if (! m_completions.isEmpty()) + return true; + + return false; +} + +bool CppCodeCompletion::completeScope(FullySpecifiedType exprTy, + const QList<TypeOfExpression::Result> &resolvedTypes, + const LookupContext &context) +{ + // Search for a class or a namespace. + foreach (TypeOfExpression::Result p, resolvedTypes) { + if (p.first->isClass() || p.first->isNamespace()) { + exprTy = p.first; + break; + } + } + + if (exprTy->asNamespace()) { + QList<Symbol *> candidates; + foreach (TypeOfExpression::Result p, resolvedTypes) { + if (Namespace *ns = p.first->asNamespace()) + candidates.append(ns); + } + completeNamespace(candidates, context); + } else if (exprTy->isClass()) { + QList<Symbol *> candidates; + foreach (TypeOfExpression::Result p, resolvedTypes) { + if (Class *k = p.first->asClass()) + candidates.append(k); + } + completeClass(candidates, context); + } + + return ! m_completions.isEmpty(); +} + +void CppCodeCompletion::addKeywords() +{ + // keyword completion items. + for (int i = T_FIRST_KEYWORD; i < T_FIRST_QT_KEYWORD; ++i) { + TextEditor::CompletionItem item(this); + item.m_text = QLatin1String(Token::name(i)); + item.m_icon = m_icons.keywordIcon(); + m_completions.append(item); + } +} + +void CppCodeCompletion::addMacros(const LookupContext &context) +{ + // macro completion items. + QSet<QByteArray> macroNames; + QSet<QString> processed; + QList<QString> todo; + todo.append(context.thisDocument()->fileName()); + while (! todo.isEmpty()) { + QString fn = todo.last(); + todo.removeLast(); + if (processed.contains(fn)) + continue; + processed.insert(fn); + if (Document::Ptr doc = context.document(fn)) { + macroNames += doc->macroNames(); + todo += doc->includedFiles(); + } + } + + foreach (const QByteArray macroName, macroNames) { + TextEditor::CompletionItem item(this); + item.m_text = QString::fromLatin1(macroName.constData(), macroName.length()); + item.m_icon = m_icons.macroIcon(); + m_completions.append(item); + } +} + +void CppCodeCompletion::addCompletionItem(Symbol *symbol) +{ + ConvertToCompletionItem toCompletionItem(this); + if (TextEditor::CompletionItem item = toCompletionItem(symbol)) + m_completions.append(item); +} + +void CppCodeCompletion::completeNamespace(const QList<Symbol *> &candidates, + const LookupContext &context) +{ + QList<Scope *> todo; + QList<Scope *> visibleScopes = context.visibleScopes(); + foreach (Symbol *candidate, candidates) { + if (Namespace *ns = candidate->asNamespace()) + context.expand(ns->members(), visibleScopes, &todo); + } + + foreach (Scope *scope, todo) { + addCompletionItem(scope->owner()); + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + addCompletionItem(scope->symbolAt(i)); + } + } +} + +void CppCodeCompletion::completeClass(const QList<Symbol *> &candidates, + const LookupContext &context, + bool staticLookup) +{ + if (candidates.isEmpty()) + return; + + Class *klass = candidates.first()->asClass(); + + QList<Scope *> todo; + context.expand(klass->members(), context.visibleScopes(), &todo); + + foreach (Scope *scope, todo) { + addCompletionItem(scope->owner()); + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *symbol = scope->symbolAt(i); + + if (symbol->type().isFriend()) + continue; + else if (! staticLookup && (symbol->isTypedef() || + symbol->isEnum() || + symbol->isClass())) + continue; + + addCompletionItem(symbol); + } + } +} + +bool CppCodeCompletion::completeQtMethod(CPlusPlus::FullySpecifiedType, + const QList<TypeOfExpression::Result> &results, + const LookupContext &context, + bool wantSignals) +{ + if (results.isEmpty()) + return false; + + ConvertToCompletionItem toCompletionItem(this); + Overview o; + o.setShowReturnTypes(false); + o.setShowArgumentNames(false); + o.setShowFunctionSignatures(true); + + QSet<QString> signatures; + foreach (TypeOfExpression::Result p, results) { + FullySpecifiedType ty = p.first; + if (ReferenceType *refTy = ty->asReferenceType()) + ty = refTy->elementType(); + if (PointerType *ptrTy = ty->asPointerType()) + ty = ptrTy->elementType(); + else + continue; // not a pointer or a reference to a pointer. + + NamedType *namedTy = ty->asNamedType(); + if (! namedTy) // not a class name. + continue; + + const QList<Scope *> visibleScopes = context.visibleScopes(p); + + const QList<Symbol *> classObjects = + context.resolveClass(namedTy->name(), visibleScopes); + + if (classObjects.isEmpty()) + continue; + + Class *klass = classObjects.first()->asClass(); + + QList<Scope *> todo; + context.expand(klass->members(), visibleScopes, &todo); + + foreach (Scope *scope, todo) { + if (! scope->isClassScope()) + continue; + + for (unsigned i = 0; i < scope->symbolCount(); ++i) { + Symbol *member = scope->symbolAt(i); + Function *fun = member->type()->asFunction(); + if (! fun) + continue; + if (wantSignals && ! fun->isSignal()) + continue; + else if (! wantSignals && ! fun->isSlot()) + continue; + if (TextEditor::CompletionItem item = toCompletionItem(fun)) { + unsigned count = fun->argumentCount(); + while (true) { + TextEditor::CompletionItem i = item; + + QString signature; + signature += overview.prettyName(fun->name()); + signature += QLatin1Char('('); + for (unsigned i = 0; i < count; ++i) { + Symbol *arg = fun->argumentAt(i); + if (i != 0) + signature += QLatin1Char(','); + signature += o.prettyType(arg->type()); + } + signature += QLatin1Char(')'); + + const QByteArray normalized = + QMetaObject::normalizedSignature(signature.toLatin1()); + + signature = QString::fromLatin1(normalized, normalized.size()); + + if (! signatures.contains(signature)) { + signatures.insert(signature); + + i.m_text = signature; // fix the completion item. + m_completions.append(i); + } + + if (count && fun->argumentAt(count - 1)->asArgument()->hasInitializer()) + --count; + else + break; + } + } + } + } + } + + return ! m_completions.isEmpty(); +} + +void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions) +{ + const int length = m_editor->position() - m_startPosition; + + if (length == 0) + *completions = m_completions; + else if (length > 0) { + const QString key = m_editor->textAt(m_startPosition, length); + + if (m_completionOperator != T_LPAREN) { + /* + * This code builds a regular expression in order to more intelligently match + * camel-case style. This means upper-case characters will be rewritten as follows: + * + * A => [a-z0-9_]*A (for any but the first capital letter) + * + * Meaning it allows any sequence of lower-case characters to preceed an + * upper-case character. So for example gAC matches getActionController. + * + * The match is case-sensitive as soon as at least one upper-case character is + * present. + */ + QString keyRegExp; + keyRegExp += QLatin1Char('^'); + bool first = true; + Qt::CaseSensitivity sensitivity = Qt::CaseInsensitive; + foreach (const QChar &c, key) { + if (c.isLower()) { + keyRegExp.append(c); + } else if (c.isUpper()) { + sensitivity = Qt::CaseSensitive; + if (!first) { + keyRegExp.append("[a-z0-9_]*"); + } + keyRegExp.append(c); + } else { + keyRegExp.append(QRegExp::escape(c)); + } + first = false; + } + const QRegExp regExp(keyRegExp, sensitivity); + + foreach (TextEditor::CompletionItem item, m_completions) { + if (regExp.indexIn(item.m_text) == 0) { + item.m_relevance = (key.length() > 0 && + item.m_text.startsWith(key, Qt::CaseInsensitive)) ? 1 : 0; + (*completions) << item; + } + } + } else if (m_completionOperator == T_LPAREN || + m_completionOperator == T_SIGNAL || + m_completionOperator == T_SLOT) { + foreach (TextEditor::CompletionItem item, m_completions) { + if (item.m_text.startsWith(key, Qt::CaseInsensitive)) { + (*completions) << item; + } + } + } + } +} + +void CppCodeCompletion::complete(const TextEditor::CompletionItem &item) +{ + Symbol *symbol = 0; + + if (item.m_data.isValid()) + symbol = item.m_data.value<Symbol *>(); + + // qDebug() << "*** complete symbol:" << symbol->fileName() << symbol->line(); + + if (m_completionOperator == T_LPAREN) { + if (symbol) { + Function *function = symbol->type()->asFunction(); + Q_ASSERT(function != 0); + + m_functionArgumentWidget = new FunctionArgumentWidget(m_core); + m_functionArgumentWidget->showFunctionHint(function); + } + } else if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { + QString toInsert = item.m_text; + toInsert += QLatin1Char(')'); + // Insert the remainder of the name + int length = m_editor->position() - m_startPosition; + m_editor->setCurPos(m_startPosition); + m_editor->replace(length, toInsert); + } else { + QString toInsert = item.m_text; + + //qDebug() << "current symbol:" << overview.prettyName(symbol->name()) + //<< overview.prettyType(symbol->type()); + + if (symbol) { + if (Function *function = symbol->type()->asFunction()) { + // If the member is a function, automatically place the opening parenthesis, + // except when it might take template parameters. + if (!function->returnType().isValid() + && (function->identity() && !function->identity()->isDestructorNameId())) { + // Don't insert any magic, since the user might have just wanted to select the class + + } else if (function->templateParameterCount() != 0) { + // If there are no arguments, then we need the template specification + if (function->argumentCount() == 0) { + toInsert.append(QLatin1Char('<')); + } + } else { + toInsert.append(QLatin1Char('(')); + + // If the function takes no arguments, automatically place the closing parenthesis + if (function->argumentCount() == 0 || (function->argumentCount() == 1 && + function->argumentAt(0)->type()->isVoidType())) { + toInsert.append(QLatin1Char(')')); + + // If the function doesn't return anything, automatically place the semicolon, + // unless we're doing a scope completion (then it might be function definition). + if (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON) { + toInsert.append(QLatin1Char(';')); + } + } + } + } + } + + // Insert the remainder of the name + int length = m_editor->position() - m_startPosition; + m_editor->setCurPos(m_startPosition); + m_editor->replace(length, toInsert); + } +} + +bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems) +{ + if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) { + return false; + } else if (completionItems.count() == 1) { + complete(completionItems.first()); + return true; + } else if (m_completionOperator != T_LPAREN) { + // Compute common prefix + QString firstKey = completionItems.first().m_text; + QString lastKey = completionItems.last().m_text; + const int length = qMin(firstKey.length(), lastKey.length()); + firstKey.truncate(length); + lastKey.truncate(length); + + while (firstKey != lastKey) { + firstKey.chop(1); + lastKey.chop(1); + } + + int typedLength = m_editor->position() - m_startPosition; + if (!firstKey.isEmpty() && firstKey.length() > typedLength) { + m_editor->setCurPos(m_startPosition); + m_editor->replace(typedLength, firstKey); + } + } + + return false; +} + +void CppCodeCompletion::cleanup() +{ + m_completions.clear(); +} + +int CppCodeCompletion::findStartOfName(const TextEditor::ITextEditor *editor) +{ + int pos = editor->position(); + QChar chr; + + // Skip to the start of a name + do { + chr = editor->characterAt(--pos); + } while (chr.isLetterOrNumber() || chr == QLatin1Char('_')); + + return pos + 1; +} diff --git a/src/plugins/cpptools/cppcodecompletion.h b/src/plugins/cpptools/cppcodecompletion.h new file mode 100644 index 0000000000..4977393521 --- /dev/null +++ b/src/plugins/cpptools/cppcodecompletion.h @@ -0,0 +1,145 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPCODECOMPLETION_H +#define CPPCODECOMPLETION_H + +// Qt +#include <QtCore/QObject> +#include <QtCore/QPointer> + +// C++ front-end +#include <ASTfwd.h> +#include <FullySpecifiedType.h> +#include <cplusplus/Icons.h> +#include <cplusplus/Overview.h> +#include <cplusplus/TypeOfExpression.h> + +// Qt Creator +#include <texteditor/icompletioncollector.h> + +namespace Core { +class ICore; +} + +namespace TextEditor { +class ITextEditor; +} + +namespace CppTools { +namespace Internal { + +class CppModelManager; +class FunctionArgumentWidget; + +class CppCodeCompletion : public TextEditor::ICompletionCollector +{ + Q_OBJECT +public: + CppCodeCompletion(CppModelManager *manager, Core::ICore *core); + + bool triggersCompletion(TextEditor::ITextEditable *editor); + int startCompletion(TextEditor::ITextEditable *editor); + void completions(QList<TextEditor::CompletionItem> *completions); + + void complete(const TextEditor::CompletionItem &item); + bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems); + void cleanup(); + + QIcon iconForSymbol(CPlusPlus::Symbol *symbol) const; + +private: + void addKeywords(); + void addMacros(const CPlusPlus::LookupContext &context); + void addCompletionItem(CPlusPlus::Symbol *symbol); + + bool completeFunction(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context); + + bool completeMember(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context); + + bool completeScope(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context); + + void completeNamespace(const QList<CPlusPlus::Symbol *> &candidates, + const CPlusPlus::LookupContext &context); + + void completeClass(const QList<CPlusPlus::Symbol *> &candidates, + const CPlusPlus::LookupContext &context, + bool staticLookup = true); + + bool completeQtMethod(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &, + const CPlusPlus::LookupContext &context, + bool wantSignals); + + bool completeSignal(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &results, + const CPlusPlus::LookupContext &context) + { return completeQtMethod(exprTy, results, context, true); } + + bool completeSlot(CPlusPlus::FullySpecifiedType exprTy, + const QList<CPlusPlus::TypeOfExpression::Result> &results, + const CPlusPlus::LookupContext &context) + { return completeQtMethod(exprTy, results, context, false); } + + static int findStartOfName(const TextEditor::ITextEditor *editor); + + QList<TextEditor::CompletionItem> m_completions; + + TextEditor::ITextEditable *m_editor; + int m_startPosition; // Position of the cursor from which completion started + + Core::ICore *m_core; + CppModelManager *m_manager; + + bool m_forcedCompletion; + + CPlusPlus::Icons m_icons; + CPlusPlus::Overview overview; + CPlusPlus::TypeOfExpression typeOfExpression; + + unsigned m_completionOperator; + + QPointer<FunctionArgumentWidget> m_functionArgumentWidget; +}; + +} // namespace Internal +} // namespace CppTools + +Q_DECLARE_METATYPE(CPlusPlus::Symbol *) + +#endif // CPPCODECOMPLETION_H diff --git a/src/plugins/cpptools/cpphoverhandler.cpp b/src/plugins/cpptools/cpphoverhandler.cpp new file mode 100644 index 0000000000..dc9bb96661 --- /dev/null +++ b/src/plugins/cpptools/cpphoverhandler.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cpphoverhandler.h" +#include "cppmodelmanager.h" + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <texteditor/itexteditor.h> +#include <debugger/debuggerconstants.h> + +#include <CoreTypes.h> +#include <FullySpecifiedType.h> +#include <Literals.h> +#include <Names.h> +#include <Scope.h> +#include <Symbol.h> +#include <Symbols.h> +#include <cplusplus/ExpressionUnderCursor.h> +#include <cplusplus/Overview.h> +#include <cplusplus/TypeOfExpression.h> + +#include <QtGui/QToolTip> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QTextCursor> +#include <QtGui/QTextBlock> +#include <QtHelp/QHelpEngineCore> +#include <QtCore/QtCore> + +using namespace CppTools::Internal; + +CppHoverHandler::CppHoverHandler(CppModelManager *manager, QObject *parent) + : QObject(parent), m_manager(manager) +{ + QFileInfo fi(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()->settings()->fileName()); + m_helpEngine = new QHelpEngineCore(fi.absolutePath() + + QLatin1String("/helpcollection.qhc"), this); + //m_helpEngine->setAutoSaveFilter(false); + m_helpEngine->setupData(); + m_helpEngine->setCurrentFilter(tr("Unfiltered")); +} + +void CppHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos) +{ + updateHelpIdAndTooltip(editor, pos); +} + +void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos) +{ + const int dbgcontext = m_manager->core()-> + uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_GDBDEBUGGER); + + if (m_manager->core()->hasContext(dbgcontext)) + return; + + if (! editor) + return; + + updateHelpIdAndTooltip(editor, pos); + + if (m_toolTip.isEmpty()) + QToolTip::hideText(); + else { + const QPoint pnt = point - QPoint(0, +#ifdef Q_WS_WIN + 24 +#else + 16 +#endif + ); + + QToolTip::showText(pnt, m_toolTip); + } +} + +static QString buildHelpId(const CPlusPlus::FullySpecifiedType &type, + const CPlusPlus::Symbol *symbol) +{ + using namespace CPlusPlus; + + Name *name = 0; + Scope *scope = 0; + + if (const Function *f = type->asFunction()) { + name = f->name(); + scope = f->scope(); + } else if (const Class *c = type->asClass()) { + name = c->name(); + scope = c->scope(); + } else if (const Enum *e = type->asEnum()) { + name = e->name(); + scope = e->scope(); + } else if (const NamedType *t = type->asNamedType()) { + name = t->name(); + } else if (const Declaration *d = symbol->asDeclaration()) { + if (d->scope() && d->scope()->owner()->isEnum()) { + name = d->name(); + scope = d->scope(); + } + } + + Overview overview; + overview.setShowArgumentNames(false); + overview.setShowReturnTypes(false); + + QStringList qualifiedNames; + qualifiedNames.prepend(overview.prettyName(name)); + + for (; scope; scope = scope->enclosingScope()) { + if (scope->owner() && scope->owner()->name() && !scope->isEnumScope()) { + Name *name = scope->owner()->name(); + Identifier *id = 0; + if (NameId *nameId = name->asNameId()) { + id = nameId->identifier(); + } else if (TemplateNameId *nameId = name->asTemplateNameId()) { + id = nameId->identifier(); + } + if (id) + qualifiedNames.prepend(QString::fromLatin1(id->chars(), id->size())); + } + } + + return qualifiedNames.join(QLatin1String("::")); +} + +void CppHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos) +{ + using namespace CPlusPlus; + + m_helpId.clear(); + m_toolTip.clear(); + + QPlainTextEdit *edit = qobject_cast<QPlainTextEdit *>(editor->widget()); + if (!edit) + return; + + QTextCursor tc(edit->document()); + tc.setPosition(pos); + + const int lineNumber = tc.block().blockNumber() + 1; + + QString fileName = editor->file()->fileName(); + Document::Ptr doc = m_manager->document(fileName); + if (doc) { + foreach (Document::DiagnosticMessage m, doc->diagnosticMessages()) { + if (m.line() == lineNumber) { + m_toolTip = m.text(); + break; + } + } + } + + if (m_toolTip.isEmpty()) { + // Move to the end of a qualified name + bool stop = false; + while (!stop) { + const QChar ch = editor->characterAt(tc.position()); + if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) + tc.setPosition(tc.position() + 1); + else if (ch == QLatin1Char(':') && editor->characterAt(tc.position() + 1) == QLatin1Char(':')) { + tc.setPosition(tc.position() + 2); + } else { + stop = true; + } + } + + // Fetch the expression's code. + ExpressionUnderCursor expressionUnderCursor; + const QString expression = expressionUnderCursor(tc); + + if (doc) { + // Find the last symbol up to the cursor position + int line = 0, column = 0; + editor->convertPosition(tc.position(), &line, &column); + Symbol *lastSymbol = doc->findSymbolAt(line, column); + + TypeOfExpression typeOfExpression; + typeOfExpression.setDocuments(m_manager->documents()); + QList<TypeOfExpression::Result> types = typeOfExpression(expression, doc, lastSymbol); + + if (!types.isEmpty()) { + FullySpecifiedType firstType = types.first().first; + FullySpecifiedType docType = firstType; + + if (const PointerType *pt = firstType->asPointerType()) { + docType = pt->elementType(); + } else if (const ReferenceType *rt = firstType->asReferenceType()) { + docType = rt->elementType(); + } + + + m_helpId = buildHelpId(docType, types.first().second); + QString displayName = buildHelpId(firstType, types.first().second); + + if (!firstType->isClass() && !firstType->isNamedType()) { + Overview overview; + overview.setShowArgumentNames(true); + overview.setShowReturnTypes(true); + m_toolTip = overview.prettyType(firstType, displayName); + } else { + m_toolTip = m_helpId; + } + } + } + } + + if (!m_helpId.isEmpty() && !m_helpEngine->linksForIdentifier(m_helpId).isEmpty()) { + m_toolTip = QString(QLatin1String("<table><tr><td valign=middle><nobr>%1</td>" + "<td><img src=\":/cpptools/images/f1.svg\"></td></tr></table>")).arg(Qt::escape(m_toolTip)); + editor->setContextHelpId(m_helpId); + } else if (!m_toolTip.isEmpty()) { + m_toolTip = QString(QLatin1String("<nobr>%1")).arg(Qt::escape(m_toolTip)); + } +} diff --git a/src/plugins/cpptools/cpphoverhandler.h b/src/plugins/cpptools/cpphoverhandler.h new file mode 100644 index 0000000000..ca828c35db --- /dev/null +++ b/src/plugins/cpptools/cpphoverhandler.h @@ -0,0 +1,75 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPHOVERHANDLER_H +#define CPPHOVERHANDLER_H + +#include <QtCore/QObject> +#include <QtCore/QPoint> + +QT_BEGIN_NAMESPACE +class QHelpEngineCore; +QT_END_NAMESPACE + +namespace TextEditor { +class ITextEditor; +} + +namespace CppTools { +namespace Internal { + +class CppModelManager; + +class CppHoverHandler : public QObject +{ + Q_OBJECT + +public: + CppHoverHandler(CppModelManager *manager, QObject *parent); + +public slots: + void showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos); + void updateContextHelpId(TextEditor::ITextEditor *editor, int pos); + +private: + void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos); + + CppModelManager *m_manager; + QHelpEngineCore *m_helpEngine; + QString m_helpId; + QString m_toolTip; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPHOVERHANDLER_H diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp new file mode 100644 index 0000000000..6ec5391267 --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -0,0 +1,740 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#define _SCL_SECURE_NO_WARNINGS 1 +#include "pp.h" + +#include "cppmodelmanager.h" +#include "cpphoverhandler.h" +#include "cpptoolsconstants.h" +#include "cpptoolseditorsupport.h" + +#include <qtconcurrent/runextensions.h> +#include <texteditor/itexteditor.h> +#include <texteditor/basetexteditor.h> + +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/session.h> + +#include <coreplugin/icore.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/progressmanager/progressmanager.h> + +#include <TranslationUnit.h> +#include <Semantic.h> +#include <AST.h> +#include <Scope.h> +#include <Literals.h> +#include <Symbols.h> +#include <Names.h> +#include <NameVisitor.h> +#include <TypeVisitor.h> +#include <Lexer.h> +#include <Token.h> + +#include <QPlainTextEdit> +#include <QTime> +#include <QDebug> + +using namespace CPlusPlus; + +namespace CppTools { +namespace Internal { + +static const char pp_configuration_file[] = "<configuration>"; + +static const char pp_configuration[] = + "# 1 \"<configuration>\"\n" + "#define __GNUC_MINOR__ 0\n" + "#define __GNUC__ 4\n" + "#define __GNUG__ 4\n" + "#define __STDC_HOSTED__ 1\n" + "#define __VERSION__ \"4.0.1 (fake)\"\n" + "#define __cplusplus 1\n" + + "#define __extension__\n" + "#define __context__\n" + "#define __range__\n" + "#define __asm(a...)\n" + "#define __asm__(a...)\n" + "#define restrict\n" + "#define __restrict\n" + + // ### add macros for win32 + "#define __cdecl\n" + "#define QT_WA(x) x\n" + "#define API\n" + "#define WINAPI\n" + "#define CALLBACK\n" + "#define STDMETHODCALLTYPE\n" + "#define __RPC_FAR\n" + "#define APIENTRY\n" + "#define __declspec(a)\n" + "#define STDMETHOD(method) virtual HRESULT STDMETHODCALLTYPE method\n"; + +class CppPreprocessor: public rpp::Client +{ +public: + CppPreprocessor(QPointer<CppModelManager> modelManager) + : m_modelManager(modelManager), + m_documents(modelManager->documents()), + m_proc(this, env) + { } + + void setWorkingCopy(const QMap<QString, QByteArray> &workingCopy) + { m_workingCopy = workingCopy; } + + void setIncludePaths(const QStringList &includePaths) + { m_includePaths = includePaths; } + + void setFrameworkPaths(const QStringList &frameworkPaths) + { m_frameworkPaths = frameworkPaths; } + + void addIncludePath(const QString &path) + { m_includePaths.append(path); } + + void setProjectFiles(const QStringList &files) + { m_projectFiles = files; } + + void operator()(QString &fileName) + { sourceNeeded(fileName, IncludeGlobal); } + +protected: + bool includeFile(const QString &absoluteFilePath, QByteArray *result) + { + if (absoluteFilePath.isEmpty() || m_included.contains(absoluteFilePath)) { + return true; + } + + if (m_workingCopy.contains(absoluteFilePath)) { + m_included.insert(absoluteFilePath); + *result = m_workingCopy.value(absoluteFilePath); + return true; + } + + QFileInfo fileInfo(absoluteFilePath); + if (! fileInfo.isFile()) + return false; + + QFile file(absoluteFilePath); + if (file.open(QFile::ReadOnly)) { + m_included.insert(absoluteFilePath); + QTextStream stream(&file); + const QString contents = stream.readAll(); + *result = contents.toUtf8(); + file.close(); + return true; + } + + return false; + } + + QByteArray tryIncludeFile(QString &fileName, IncludeType type) + { + QFileInfo fileInfo(fileName); + if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) { + QByteArray contents; + includeFile(fileName, &contents); + return contents; + } + + if (type == IncludeLocal && m_currentDoc) { + QFileInfo currentFileInfo(m_currentDoc->fileName()); + QString path = currentFileInfo.absolutePath(); + path += QLatin1Char('/'); + path += fileName; + path = QDir::cleanPath(path); + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + + foreach (const QString &includePath, m_includePaths) { + QString path = includePath; + path += QLatin1Char('/'); + path += fileName; + path = QDir::cleanPath(path); + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + + // look in the system include paths + foreach (const QString &includePath, m_systemIncludePaths) { + QString path = includePath; + path += QLatin1Char('/'); + path += fileName; + path = QDir::cleanPath(path); + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + + int index = fileName.indexOf(QLatin1Char('/')); + if (index != -1) { + QString frameworkName = fileName.left(index); + QString name = fileName.mid(index + 1); + + foreach (const QString &frameworkPath, m_frameworkPaths) { + QString path = frameworkPath; + path += QLatin1Char('/'); + path += frameworkName; + path += QLatin1String(".framework/Headers/"); + path += name; + QByteArray contents; + if (includeFile(path, &contents)) { + fileName = path; + return contents; + } + } + } + + QString path = fileName; + if (path.at(0) != QLatin1Char('/')) + path.prepend(QLatin1Char('/')); + + foreach (const QString &projectFile, m_projectFiles) { + if (projectFile.endsWith(path)) { + fileName = projectFile; + QByteArray contents; + includeFile(fileName, &contents); + return contents; + } + } + + //qDebug() << "**** file" << fileName << "not found!"; + return QByteArray(); + } + + virtual void macroAdded(const QByteArray ¯oName, const QByteArray ¯oText) + { + if (! m_currentDoc) + return; + + m_currentDoc->appendMacro(macroName, macroText); + } + + void mergeEnvironment(Document::Ptr doc) + { + QSet<QString> processed; + mergeEnvironment(doc, &processed); + } + + void mergeEnvironment(Document::Ptr doc, QSet<QString> *processed) + { + if (! doc) + return; + + const QString fn = doc->fileName(); + + if (processed->contains(fn)) + return; + + processed->insert(fn); + + foreach (QString includedFile, doc->includedFiles()) + mergeEnvironment(m_documents.value(includedFile), processed); + + const QByteArray macros = doc->definedMacros(); + QByteArray localFileName = doc->fileName().toUtf8(); + + QByteArray dummy; + m_proc(localFileName, macros, &dummy); + } + + virtual void startSkippingBlocks(unsigned offset) + { + //qDebug() << "start skipping blocks:" << offset; + if (m_currentDoc) + m_currentDoc->startSkippingBlocks(offset); + } + + virtual void stopSkippingBlocks(unsigned offset) + { + //qDebug() << "stop skipping blocks:" << offset; + if (m_currentDoc) + m_currentDoc->stopSkippingBlocks(offset); + } + + virtual void sourceNeeded(QString &fileName, IncludeType type) + { + if (fileName.isEmpty()) + return; + + QByteArray contents = tryIncludeFile(fileName, type); + + if (m_currentDoc) { + m_currentDoc->addIncludeFile(fileName); + if (contents.isEmpty() && ! QFileInfo(fileName).isAbsolute()) { + QString msg; + msg += fileName; + msg += QLatin1String(": No such file or directory"); + Document::DiagnosticMessage d(Document::DiagnosticMessage::Warning, + m_currentDoc->fileName(), + env.currentLine, /*column = */ 0, + msg); + m_currentDoc->addDiagnosticMessage(d); + //qWarning() << "file not found:" << fileName << m_currentDoc->fileName() << env.current_line; + } + } + + if (! contents.isEmpty()) { + Document::Ptr cachedDoc = m_documents.value(fileName); + if (cachedDoc && m_currentDoc) { + mergeEnvironment(cachedDoc); + } else { + Document::Ptr previousDoc = switchDocument(Document::create(fileName)); + + const QByteArray previousFile = env.current_file; + const unsigned previousLine = env.currentLine; + + env.current_file = QByteArray(m_currentDoc->translationUnit()->fileName(), + m_currentDoc->translationUnit()->fileNameLength()); + + QByteArray preprocessedCode; + m_proc(contents, &preprocessedCode); + //qDebug() << preprocessedCode; + + env.current_file = previousFile; + env.currentLine = previousLine; + + m_currentDoc->setSource(preprocessedCode); + m_currentDoc->parse(); + m_currentDoc->check(); + m_currentDoc->releaseTranslationUnit(); // release the AST and the token stream. + + if (m_modelManager) + m_modelManager->emitDocumentUpdated(m_currentDoc); + (void) switchDocument(previousDoc); + } + } + } + + Document::Ptr switchDocument(Document::Ptr doc) + { + Document::Ptr previousDoc = m_currentDoc; + m_currentDoc = doc; + return previousDoc; + } + +private: + QPointer<CppModelManager> m_modelManager; + CppModelManager::DocumentTable m_documents; + rpp::Environment env; + rpp::pp m_proc; + QStringList m_includePaths; + QStringList m_systemIncludePaths; + QMap<QString, QByteArray> m_workingCopy; + QStringList m_projectFiles; + QStringList m_frameworkPaths; + QSet<QString> m_included; + Document::Ptr m_currentDoc; +}; + +} // namespace Internal +} // namespace CppTools + + +using namespace CppTools; +using namespace CppTools::Internal; + +/*! + \class CppTools::CppModelManager + \brief The CppModelManager keeps track of one CppCodeModel instance + for each project and all related CppCodeModelPart instances. + + It also takes care of updating the code models when C++ files are + modified within Workbench. +*/ + +CppModelManager::CppModelManager(QObject *parent) : + CppModelManagerInterface(parent), + m_core(ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>()) +{ + m_projectExplorer = ExtensionSystem::PluginManager::instance() + ->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + + Q_ASSERT(m_projectExplorer); + + ProjectExplorer::SessionManager *session = m_projectExplorer->session(); + Q_ASSERT(session != 0); + + connect(session, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project *)), + this, SLOT(onAboutToRemoveProject(ProjectExplorer::Project *))); + + connect(session, SIGNAL(sessionUnloaded()), + this, SLOT(onSessionUnloaded())); + + qRegisterMetaType<CPlusPlus::Document::Ptr>("CPlusPlus::Document::Ptr"); + + // thread connections + connect(this, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)), + this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr))); + + m_hoverHandler = new CppHoverHandler(this, this); + + // Listen for editor closed and opened events so that we can keep track of changing files + connect(m_core->editorManager(), SIGNAL(editorOpened(Core::IEditor *)), + this, SLOT(editorOpened(Core::IEditor *))); + + connect(m_core->editorManager(), SIGNAL(editorAboutToClose(Core::IEditor *)), + this, SLOT(editorAboutToClose(Core::IEditor *))); +} + +CppModelManager::~CppModelManager() +{ } + +Document::Ptr CppModelManager::document(const QString &fileName) +{ return m_documents.value(fileName); } + +CppModelManager::DocumentTable CppModelManager::documents() +{ return m_documents; } + +QStringList CppModelManager::projectFiles() const +{ + QStringList files; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + files += pinfo.sourceFiles; + } + return files; +} + +QStringList CppModelManager::includePaths() const +{ + QStringList includePaths; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + includePaths += pinfo.includePaths; + } + return includePaths; +} + +QStringList CppModelManager::frameworkPaths() const +{ + QStringList frameworkPaths; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + frameworkPaths += pinfo.frameworkPaths; + } + return frameworkPaths; +} + +QByteArray CppModelManager::definedMacros() const +{ + QByteArray macros; + QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects); + while (it.hasNext()) { + it.next(); + ProjectInfo pinfo = it.value(); + macros += pinfo.defines; + } + return macros; +} + +QMap<QString, QByteArray> CppModelManager::buildWorkingCopyList() const +{ + QMap<QString, QByteArray> workingCopy; + QMapIterator<TextEditor::ITextEditor *, CppEditorSupport *> it(m_editorSupport); + while (it.hasNext()) { + it.next(); + TextEditor::ITextEditor *textEditor = it.key(); + CppEditorSupport *editorSupport = it.value(); + QString fileName = textEditor->file()->fileName(); + workingCopy[fileName] = editorSupport->contents().toUtf8(); + } + + // add the project configuration file + QByteArray conf(pp_configuration); + conf += definedMacros(); + workingCopy[pp_configuration_file] = conf; + + return workingCopy; +} + +void CppModelManager::updateSourceFiles(const QStringList &sourceFiles) +{ (void) refreshSourceFiles(sourceFiles); } + +CppModelManager::ProjectInfo *CppModelManager::projectInfo(ProjectExplorer::Project *project) +{ return &m_projects[project]; } + +QFuture<void> CppModelManager::refreshSourceFiles(const QStringList &sourceFiles) +{ + if (qgetenv("QTCREATOR_NO_CODE_INDEXER").isNull()) { + const QMap<QString, QByteArray> workingCopy = buildWorkingCopyList(); + + QFuture<void> result = QtConcurrent::run(&CppModelManager::parse, this, + sourceFiles, workingCopy); + + if (sourceFiles.count() > 1) { + m_core->progressManager()->addTask(result, tr("Indexing"), + CppTools::Constants::TASK_INDEX, + Core::ProgressManagerInterface::CloseOnSuccess); + } + return result; + } + return QFuture<void>(); +} + +/*! + \fn void CppModelManager::editorOpened(Core::IEditor *editor) + \brief If a C++ editor is opened, the model manager listens to content changes + in order to update the CppCodeModel accordingly. It also updates the + CppCodeModel for the first time with this editor. + + \sa void CppModelManager::editorContentsChanged() + */ +void CppModelManager::editorOpened(Core::IEditor *editor) +{ + if (isCppEditor(editor)) { + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); + Q_ASSERT(textEditor != 0); + + CppEditorSupport *editorSupport = new CppEditorSupport(this); + editorSupport->setTextEditor(textEditor); + m_editorSupport[textEditor] = editorSupport; + + // ### move in CppEditorSupport + connect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*, QPoint, int)), + m_hoverHandler, SLOT(showToolTip(TextEditor::ITextEditor*, QPoint, int))); + + // ### move in CppEditorSupport + connect(editor, SIGNAL(contextHelpIdRequested(TextEditor::ITextEditor*, int)), + m_hoverHandler, SLOT(updateContextHelpId(TextEditor::ITextEditor*, int))); + } +} + +void CppModelManager::editorAboutToClose(Core::IEditor *editor) +{ + if (isCppEditor(editor)) { + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); + Q_ASSERT(textEditor != 0); + + CppEditorSupport *editorSupport = m_editorSupport.value(textEditor); + m_editorSupport.remove(textEditor); + delete editorSupport; + } +} + +bool CppModelManager::isCppEditor(Core::IEditor *editor) const +{ + Core::UniqueIDManager *uidm = m_core->uniqueIDManager(); + const int uid = uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); + return editor->context().contains(uid); +} + +void CppModelManager::emitDocumentUpdated(Document::Ptr doc) +{ emit documentUpdated(doc); } + +void CppModelManager::onDocumentUpdated(Document::Ptr doc) +{ + const QString fileName = doc->fileName(); + m_documents[fileName] = doc; + QList<Core::IEditor *> openedEditors = m_core->editorManager()->openedEditors(); + foreach (Core::IEditor *editor, openedEditors) { + if (editor->file()->fileName() == fileName) { + TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor); + if (! textEditor) + continue; + + TextEditor::BaseTextEditor *ed = qobject_cast<TextEditor::BaseTextEditor *>(textEditor->widget()); + if (! ed) + continue; + + QList<TextEditor::BaseTextEditor::BlockRange> blockRanges; + + foreach (const Document::Block block, doc->skippedBlocks()) { + blockRanges.append(TextEditor::BaseTextEditor::BlockRange(block.begin(), block.end())); + } + ed->setIfdefedOutBlocks(blockRanges); + + QList<QTextEdit::ExtraSelection> selections; + + // set up the format for the errors + QTextCharFormat errorFormat; + errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + errorFormat.setUnderlineColor(Qt::red); + + // set up the format for the warnings. + QTextCharFormat warningFormat; + warningFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + warningFormat.setUnderlineColor(Qt::darkYellow); + + QSet<int> lines; + foreach (const Document::DiagnosticMessage m, doc->diagnosticMessages()) { + if (m.fileName() != fileName) + continue; + else if (lines.contains(m.line())) + continue; + else if (lines.size() == MAX_SELECTION_COUNT) + break; // we're done. + + lines.insert(m.line()); + + QTextEdit::ExtraSelection sel; + if (m.isWarning()) + sel.format = warningFormat; + else + sel.format = errorFormat; + + QTextCursor c(ed->document()->findBlockByNumber(m.line() - 1)); + const QString text = c.block().text(); + for (int i = 0; i < text.size(); ++i) { + if (! text.at(i).isSpace()) { + c.setPosition(c.position() + i); + break; + } + } + c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + sel.cursor = c; + selections.append(sel); + } + ed->setExtraExtraSelections(selections); + break; + } + } +} + +void CppModelManager::onAboutToRemoveProject(ProjectExplorer::Project *project) +{ + m_projects.remove(project); + GC(); +} + +void CppModelManager::onSessionUnloaded() +{ + if (m_core->progressManager()) + m_core->progressManager()->cancelTasks(CppTools::Constants::TASK_INDEX); +} + +void CppModelManager::parse(QFutureInterface<void> &future, + CppModelManager *model, + QStringList files, + QMap<QString, QByteArray> workingCopy) +{ + // Change the priority of the background parser thread to idle. + QThread::currentThread()->setPriority(QThread::IdlePriority); + + future.setProgressRange(0, files.size()); + + CppPreprocessor preproc(model); + preproc.setWorkingCopy(workingCopy); + preproc.setProjectFiles(model->projectFiles()); + preproc.setIncludePaths(model->includePaths()); + preproc.setFrameworkPaths(model->frameworkPaths()); + + QString conf = QLatin1String(pp_configuration_file); + (void) preproc(conf); + + const int STEP = 10; + + for (int i = 0; i < files.size(); ++i) { + if (future.isPaused()) + future.waitForResume(); + + if (future.isCanceled()) + break; + + future.setProgressValue(i); + +#ifdef CPPTOOLS_DEBUG_PARSING_TIME + QTime tm; + tm.start(); +#endif + + QString fileName = files.at(i); + preproc(fileName); + + if (! (i % STEP)) // Yields execution of the current thread. + QThread::yieldCurrentThread(); + +#ifdef CPPTOOLS_DEBUG_PARSING_TIME + qDebug() << fileName << "parsed in:" << tm.elapsed(); +#endif + } + + // Restore the previous thread priority. + QThread::currentThread()->setPriority(QThread::NormalPriority); +} + +void CppModelManager::GC() +{ + DocumentTable documents = m_documents; + + QSet<QString> processed; + QStringList todo = m_projectFiles; + + while (! todo.isEmpty()) { + QString fn = todo.last(); + todo.removeLast(); + + if (processed.contains(fn)) + continue; + + processed.insert(fn); + + if (Document::Ptr doc = documents.value(fn)) { + todo += doc->includedFiles(); + } + } + + QStringList removedFiles; + QMutableMapIterator<QString, Document::Ptr> it(documents); + while (it.hasNext()) { + it.next(); + const QString fn = it.key(); + if (! processed.contains(fn)) { + removedFiles.append(fn); + it.remove(); + } + } + + emit aboutToRemoveFiles(removedFiles); + m_documents = documents; +} + + diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h new file mode 100644 index 0000000000..bed882f9cd --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -0,0 +1,135 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPMODELMANAGER_H +#define CPPMODELMANAGER_H + +#include <cpptools/cppmodelmanagerinterface.h> +#include <projectexplorer/project.h> +#include <cplusplus/CppDocument.h> + +#include <QMap> +#include <QFutureInterface> + +namespace Core { +class ICore; +class IEditor; +} + +namespace TextEditor { +class ITextEditor; +} + +namespace ProjectExplorer { +class ProjectExplorerPlugin; +} + +namespace CppTools { +namespace Internal { + +class CppEditorSupport; +class CppHoverHandler; + +class CppModelManager : public CppModelManagerInterface +{ + Q_OBJECT + +public: + CppModelManager(QObject *parent); + virtual ~CppModelManager(); + + virtual void updateSourceFiles(const QStringList &sourceFiles); + virtual ProjectInfo *projectInfo(ProjectExplorer::Project *project); + virtual CPlusPlus::Document::Ptr document(const QString &fileName); + virtual DocumentTable documents(); + virtual void GC(); + + QFuture<void> refreshSourceFiles(const QStringList &sourceFiles); + + inline Core::ICore *core() const { return m_core; } + + bool isCppEditor(Core::IEditor *editor) const; // ### private + + void emitDocumentUpdated(CPlusPlus::Document::Ptr doc); + +Q_SIGNALS: + void projectPathChanged(const QString &projectPath); + + void documentUpdated(CPlusPlus::Document::Ptr doc); + void aboutToRemoveFiles(const QStringList &files); + +public Q_SLOTS: + void editorOpened(Core::IEditor *editor); + void editorAboutToClose(Core::IEditor *editor); + +private Q_SLOTS: + // this should be executed in the GUI thread. + void onDocumentUpdated(CPlusPlus::Document::Ptr doc); + void onAboutToRemoveProject(ProjectExplorer::Project *project); + void onSessionUnloaded(); + +private: + QMap<QString, QByteArray> buildWorkingCopyList() const; + QStringList projectFiles() const; + QStringList includePaths() const; + QStringList frameworkPaths() const; + QByteArray definedMacros() const; + + static void parse(QFutureInterface<void> &future, + CppModelManager *model, + QStringList files, + QMap<QString, QByteArray> workingCopy); + +private: + Core::ICore *m_core; + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + CppHoverHandler *m_hoverHandler; + DocumentTable m_documents; + + // List of available source files + QStringList m_projectFiles; + + // editor integration + QMap<TextEditor::ITextEditor *, CppEditorSupport *> m_editorSupport; + + // project integration + QMap<ProjectExplorer::Project *, ProjectInfo> m_projects; + + enum { + MAX_SELECTION_COUNT = 5 + }; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPMODELMANAGER_H diff --git a/src/plugins/cpptools/cppmodelmanagerinterface.h b/src/plugins/cpptools/cppmodelmanagerinterface.h new file mode 100644 index 0000000000..3b93c346c5 --- /dev/null +++ b/src/plugins/cpptools/cppmodelmanagerinterface.h @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPMODELMANAGERINTERFACE_H +#define CPPMODELMANAGERINTERFACE_H + +#include <cpptools/cpptools_global.h> +#include <cplusplus/CppDocument.h> +#include <QtCore/QObject> +#include <QtCore/QMap> + +namespace ProjectExplorer { + class Project; +} + +namespace CppTools { + +class CPPTOOLS_EXPORT CppModelManagerInterface + : public QObject +{ + Q_OBJECT + +public: + typedef QMap<QString, CPlusPlus::Document::Ptr> DocumentTable; + + struct ProjectInfo + { + QString projectPath; + QByteArray defines; + QStringList sourceFiles; + QStringList includePaths; + QStringList frameworkPaths; + }; + +public: + CppModelManagerInterface(QObject *parent = 0) : QObject(parent) {} + virtual ~CppModelManagerInterface() {} + + virtual void GC() = 0; + virtual void updateSourceFiles(const QStringList &sourceFiles) = 0; + + virtual CPlusPlus::Document::Ptr document(const QString &fileName) = 0; + virtual DocumentTable documents() = 0; + + virtual ProjectInfo *projectInfo(ProjectExplorer::Project *project) = 0; +}; + +} // namespace CppTools + +#endif // CPPMODELMANAGERINTERFACE_H diff --git a/src/plugins/cpptools/cppquickopenfilter.cpp b/src/plugins/cpptools/cppquickopenfilter.cpp new file mode 100644 index 0000000000..98086d3f81 --- /dev/null +++ b/src/plugins/cpptools/cppquickopenfilter.cpp @@ -0,0 +1,280 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cppquickopenfilter.h" + +#include <Literals.h> +#include <Symbols.h> +#include <SymbolVisitor.h> +#include <Scope.h> +#include <cplusplus/Overview.h> +#include <cplusplus/Icons.h> + +#include <coreplugin/editormanager/ieditor.h> +#include <texteditor/itexteditor.h> +#include <texteditor/basetexteditor.h> + +#include <QtCore/QMultiMap> + +#include <functional> + +using namespace CPlusPlus; + +namespace CppTools { +namespace Internal { + +class SearchSymbols: public std::unary_function<Document::Ptr, QList<ModelItemInfo> >, + protected SymbolVisitor +{ + Overview overview; + Icons icons; + QList<ModelItemInfo> items; + +public: + QList<ModelItemInfo> operator()(Document::Ptr doc) + { return operator()(doc, QString()); } + + QList<ModelItemInfo> operator()(Document::Ptr doc, const QString &scope) + { + QString previousScope = switchScope(scope); + items.clear(); + for (unsigned i = 0; i < doc->globalSymbolCount(); ++i) { + accept(doc->globalSymbolAt(i)); + } + (void) switchScope(previousScope); + return items; + } + +protected: + using SymbolVisitor::visit; + + void accept(Symbol *symbol) + { Symbol::visitSymbol(symbol, this); } + + QString switchScope(const QString &scope) + { + QString previousScope = _scope; + _scope = scope; + return previousScope; + } + + virtual bool visit(Enum *symbol) + { + QString name = symbolName(symbol); + QString previousScope = switchScope(name); + QIcon icon = icons.iconForSymbol(symbol); + Scope *members = symbol->members(); + items.append(ModelItemInfo(name, QString(), ModelItemInfo::Enum, + QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), + symbol->line(), + icon)); + for (unsigned i = 0; i < members->symbolCount(); ++i) { + accept(members->symbolAt(i)); + } + (void) switchScope(previousScope); + return false; + } + + virtual bool visit(Function *symbol) + { + QString name = symbolName(symbol); + QString type = overview.prettyType(symbol->type()); + QIcon icon = icons.iconForSymbol(symbol); + items.append(ModelItemInfo(name, type, ModelItemInfo::Method, + QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), + symbol->line(), + icon)); + return false; + } + + virtual bool visit(Namespace *symbol) + { + QString name = symbolName(symbol); + QString previousScope = switchScope(name); + Scope *members = symbol->members(); + for (unsigned i = 0; i < members->symbolCount(); ++i) { + accept(members->symbolAt(i)); + } + (void) switchScope(previousScope); + return false; + } +#if 0 + // This visit method would make function declaration be included in QuickOpen + virtual bool visit(Declaration *symbol) + { + if (symbol->type()->isFunction()) { + QString name = symbolName(symbol); + QString type = overview.prettyType(symbol->type()); + //QIcon icon = ...; + items.append(ModelItemInfo(name, type, ModelItemInfo::Method, + QString::fromUtf8(symbol->fileName(), symbol->line()), + symbol->line())); + } + return false; + } +#endif + virtual bool visit(Class *symbol) + { + QString name = symbolName(symbol); + QString previousScope = switchScope(name); + QIcon icon = icons.iconForSymbol(symbol); + items.append(ModelItemInfo(name, QString(), ModelItemInfo::Class, + QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()), + symbol->line(), + icon)); + Scope *members = symbol->members(); + for (unsigned i = 0; i < members->symbolCount(); ++i) { + accept(members->symbolAt(i)); + } + (void) switchScope(previousScope); + return false; + } + + QString symbolName(Symbol *symbol) const + { + QString name = _scope; + if (! name.isEmpty()) + name += QLatin1String("::"); + QString symbolName = overview.prettyName(symbol->name()); + if (symbolName.isEmpty()) { + QString type; + if (symbol->isNamespace()) { + type = QLatin1String("namespace"); + } else if (symbol->isEnum()) { + type = QLatin1String("enum"); + } else if (Class *c = symbol->asClass()) { + if (c->isUnion()) { + type = QLatin1String("union"); + } else if (c->isStruct()) { + type = QLatin1String("struct"); + } else { + type = QLatin1String("class"); + } + } else { + type = QLatin1String("symbol"); + } + symbolName = QLatin1String("<anonymous "); + symbolName += type; + symbolName += QLatin1String(">"); + } + name += symbolName; + return name; + } + +private: + QString _scope; +}; + +} // namespace Internal +} // namespace CppTools + +using namespace CppTools::Internal; + +CppQuickOpenFilter::CppQuickOpenFilter(CppModelManager *manager, Core::EditorManager *editorManager) + : m_manager(manager), + m_editorManager(editorManager), + m_forceNewSearchList(true) +{ + setShortcutString(":"); + setIncludedByDefault(false); + + connect(manager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)), + this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr))); + + connect(manager, SIGNAL(aboutToRemoveFiles(QStringList)), + this, SLOT(onAboutToRemoveFiles(QStringList))); +} + +CppQuickOpenFilter::~CppQuickOpenFilter() +{ } + +void CppQuickOpenFilter::onDocumentUpdated(CPlusPlus::Document::Ptr doc) +{ + m_searchList[doc->fileName()] = Info(doc); +} + +void CppQuickOpenFilter::onAboutToRemoveFiles(const QStringList &files) +{ + foreach (QString file, files) { + m_searchList.remove(file); + } +} + +void CppQuickOpenFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); +} + +QList<QuickOpen::FilterEntry> CppQuickOpenFilter::matchesFor(const QString &origEntry) +{ + QString entry = trimWildcards(origEntry); + QList<QuickOpen::FilterEntry> entries; + QStringMatcher matcher(entry, Qt::CaseInsensitive); + const QRegExp regexp("*"+entry+"*", Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.isValid()) + return entries; + bool hasWildcard = (entry.contains('*') || entry.contains('?')); + + SearchSymbols search; + QMutableMapIterator<QString, Info> it(m_searchList); + while (it.hasNext()) { + it.next(); + + Info info = it.value(); + if (info.dirty) { + info.dirty = false; + info.items = search(info.doc); + it.setValue(info); + } + + QList<ModelItemInfo> items = info.items; + + foreach (ModelItemInfo info, items) { + if ((hasWildcard && regexp.exactMatch(info.symbolName)) + || (!hasWildcard && matcher.indexIn(info.symbolName) != -1)) { + QVariant id = qVariantFromValue(info); + QuickOpen::FilterEntry filterEntry(this, info.symbolName, id, info.icon); + filterEntry.extraInfo = info.symbolType; + entries.append(filterEntry); + } + } + } + + return entries; +} + +void CppQuickOpenFilter::accept(QuickOpen::FilterEntry selection) const +{ + ModelItemInfo info = qvariant_cast<CppTools::Internal::ModelItemInfo>(selection.internalData); + + TextEditor::BaseTextEditor::openEditorAt(info.fileName, info.line); +} diff --git a/src/plugins/cpptools/cppquickopenfilter.h b/src/plugins/cpptools/cppquickopenfilter.h new file mode 100644 index 0000000000..1375e468f5 --- /dev/null +++ b/src/plugins/cpptools/cppquickopenfilter.h @@ -0,0 +1,118 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPQUICKOPENFILTER_H +#define CPPQUICKOPENFILTER_H + +#include "cppmodelmanager.h" +#include <cplusplus/CppDocument.h> +#include <coreplugin/editormanager/editormanager.h> +#include <quickopen/iquickopenfilter.h> +#include <QtGui/QIcon> +#include <QFile> +#include <QMetaType> + +namespace CppTools { +namespace Internal { + +struct ModelItemInfo +{ + enum ItemType { Enum, Class, Method }; + + ModelItemInfo() + { } + + ModelItemInfo(const QString &symbolName, + const QString &symbolType, + ItemType type, + const QString &fileName, + int line, + const QIcon &icon) + : symbolName(symbolName), + symbolType(symbolType), + type(type), + fileName(fileName), + line(line), + icon(icon) + { } + + QString symbolName; + QString symbolType; + ItemType type; + QString fileName; + int line; + QIcon icon; +}; + +class CppQuickOpenFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT +public: + CppQuickOpenFilter(CppModelManager *manager, Core::EditorManager *editorManager); + ~CppQuickOpenFilter(); + + QString trName() const { return tr("Classes and Methods"); } + QString name() const { return "Classes and Methods"; } + Priority priority() const { return Medium; } + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + void refresh(QFutureInterface<void> &future); + +private slots: + void onDocumentUpdated(CPlusPlus::Document::Ptr doc); + void onAboutToRemoveFiles(const QStringList &files); + +private: + CppModelManager *m_manager; + Core::EditorManager *m_editorManager; + + struct Info { + Info(): dirty(true) {} + Info(CPlusPlus::Document::Ptr doc): doc(doc), dirty(true) {} + + CPlusPlus::Document::Ptr doc; + QList<ModelItemInfo> items; + bool dirty; + }; + + QMap<QString, Info> m_searchList; + QList<ModelItemInfo> m_previousResults; + bool m_forceNewSearchList; + QString m_previousEntry; +}; + +} // namespace Internal +} // namespace CppTools + +Q_DECLARE_METATYPE(CppTools::Internal::ModelItemInfo) + +#endif // CPPQUICKOPENFILTER_H diff --git a/src/plugins/cpptools/cpptools.cpp b/src/plugins/cpptools/cpptools.cpp new file mode 100644 index 0000000000..ceca43afaa --- /dev/null +++ b/src/plugins/cpptools/cpptools.cpp @@ -0,0 +1,256 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "cpptools.h" +#include "cppcodecompletion.h" +#include "cpphoverhandler.h" +#include "cppmodelmanager.h" +#include "cpptoolsconstants.h" +#include "cppquickopenfilter.h" + +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <cppeditor/cppeditorconstants.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +using namespace CppTools::Internal; + +enum { debug = 0 }; + + +CppToolsPlugin *CppToolsPlugin::m_instance = 0; + +CppToolsPlugin::CppToolsPlugin() : + m_core(0), + m_context(-1), + m_modelManager(0) +{ + m_instance = this; +} + +CppToolsPlugin::~CppToolsPlugin() +{ + m_instance = 0; + m_modelManager = 0; // deleted automatically +} + +bool CppToolsPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + Core::ActionManagerInterface *am = m_core->actionManager(); + + // Objects + m_modelManager = new CppModelManager(this); + addAutoReleasedObject(m_modelManager); + CppCodeCompletion *cppcodecompletion = new CppCodeCompletion(m_modelManager, m_core); + addAutoReleasedObject(cppcodecompletion); + CppQuickOpenFilter *quickOpenFilter = new CppQuickOpenFilter(m_modelManager, + m_core->editorManager()); + addAutoReleasedObject(quickOpenFilter); + + // Menus + Core::IActionContainer *mtools = am->actionContainer(Core::Constants::M_TOOLS); + Core::IActionContainer *mcpptools = am->createMenu(CppTools::Constants::M_TOOLS_CPP); + QMenu *menu = mcpptools->menu(); + menu->setTitle(tr("&C++")); + menu->setEnabled(true); + mtools->addMenu(mcpptools); + + // Actions + m_context = m_core->uniqueIDManager()->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR); + QList<int> context = QList<int>() << m_context; + + QAction *switchAction = new QAction(tr("Switch Header/Source"), this); + Core::ICommand *command = am->registerAction(switchAction, Constants::SWITCH_HEADER_SOURCE, context); + command->setDefaultKeySequence(QKeySequence(Qt::Key_F4)); + mcpptools->addAction(command); + connect(switchAction, SIGNAL(triggered()), this, SLOT(switchHeaderSource())); + + return true; +} + +void CppToolsPlugin::extensionsInitialized() +{ +} + +void CppToolsPlugin::switchHeaderSource() +{ + if (!m_core) + return; + + Core::IEditor *editor = m_core->editorManager()->currentEditor(); + QString otherFile = correspondingHeaderOrSource(editor->file()->fileName()); + if (!otherFile.isEmpty()) { + m_core->editorManager()->openEditor(otherFile); + m_core->editorManager()->ensureEditorManagerVisible(); + } +} + +QFileInfo CppToolsPlugin::findFile(const QDir &dir, const QString &name, + const ProjectExplorer::Project *project) const +{ + if (debug) + qDebug() << Q_FUNC_INFO << dir << name; + + if (project) { + QString pattern = QString(1, QLatin1Char('/')); + pattern += name; + const QStringList projectFiles = project->files(ProjectExplorer::Project::AllFiles); + const QStringList::const_iterator pcend = projectFiles.constEnd(); + for (QStringList::const_iterator it = projectFiles.constBegin(); it != pcend; ++it) + if (it->endsWith(pattern)) + return QFileInfo(*it); + return QFileInfo(); + } + return QFileInfo(dir, name); +} + +// Figure out file type +enum FileType { HeaderFile, C_SourceFile, CPP_SourceFile, UnknownType }; + +static inline FileType fileType(const Core::MimeDatabase *mimeDatase, const QFileInfo & fi) +{ + const Core::MimeType mimeType = mimeDatase->findByFile(fi); + if (!mimeType) + return UnknownType; + const QString typeName = mimeType.type(); + if (typeName == QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)) + return C_SourceFile; + if (typeName == QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)) + return CPP_SourceFile; + if (typeName == QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE) + || typeName == QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)) + return HeaderFile; + return UnknownType; +} + +// Return the suffixes that should be checked when trying to find a +// source belonging to a header and vice versa +static QStringList matchingCandidateSuffixes(const Core::MimeDatabase *mimeDatase, FileType type) +{ + switch (type) { + case UnknownType: + break; + case HeaderFile: // Note that C/C++ headers are undistinguishable + return mimeDatase->findByType(QLatin1String(CppTools::Constants::C_SOURCE_MIMETYPE)).suffixes() + + mimeDatase->findByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)).suffixes(); + case C_SourceFile: + return mimeDatase->findByType(QLatin1String(CppTools::Constants::C_HEADER_MIMETYPE)).suffixes(); + case CPP_SourceFile: + return mimeDatase->findByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)).suffixes(); + } + return QStringList(); +} + +QString CppToolsPlugin::correspondingHeaderOrSourceI(const QString &fileName) const +{ + const Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + const Core::MimeDatabase *mimeDatase = core->mimeDatabase(); + ProjectExplorer::ProjectExplorerPlugin *explorer = + ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); + ProjectExplorer::Project *project = (explorer ? explorer->currentProject() : 0); + + const QFileInfo fi(fileName); + const FileType type = fileType(mimeDatase, fi); + + if (debug) + qDebug() << Q_FUNC_INFO << fileName << type; + + if (type == UnknownType) + return QString(); + + const QDir absoluteDir = fi.absoluteDir(); + const QString baseName = fi.baseName(); + const QStringList suffixes = matchingCandidateSuffixes(mimeDatase, type); + + const QString privateHeaderSuffix = QLatin1String("_p"); + const QChar dot = QLatin1Char('.'); + QStringList candidates; + // Check base matches 'source.h'-> 'source.cpp' and vice versa + const QStringList::const_iterator scend = suffixes.constEnd(); + for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) { + QString candidate = baseName; + candidate += dot; + candidate += *it; + const QFileInfo candidateFi = findFile(absoluteDir, candidate, project); + if (candidateFi.isFile()) + return candidateFi.absoluteFilePath(); + } + if (type == HeaderFile) { + // 'source_p.h': try 'source.cpp' + if (baseName.endsWith(privateHeaderSuffix)) { + QString sourceBaseName = baseName; + sourceBaseName.truncate(sourceBaseName.size() - privateHeaderSuffix.size()); + for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) { + QString candidate = sourceBaseName; + candidate += dot; + candidate += *it; + const QFileInfo candidateFi = findFile(absoluteDir, candidate, project); + if (candidateFi.isFile()) + return candidateFi.absoluteFilePath(); + } + } + } else { + // 'source.cpp': try 'source_p.h' + const QStringList::const_iterator scend = suffixes.constEnd(); + for (QStringList::const_iterator it = suffixes.constBegin(); it != scend; ++it) { + QString candidate = baseName; + candidate += privateHeaderSuffix; + candidate += dot; + candidate += *it; + const QFileInfo candidateFi = findFile(absoluteDir, candidate, project); + if (candidateFi.isFile()) + return candidateFi.absoluteFilePath(); + } + } + return QString(); +} + +QString CppToolsPlugin::correspondingHeaderOrSource(const QString &fileName) const +{ + const QString rc = correspondingHeaderOrSourceI(fileName); + if (debug) + qDebug() << Q_FUNC_INFO << fileName << rc; + return rc; +} + +Q_EXPORT_PLUGIN(CppToolsPlugin) diff --git a/src/plugins/cpptools/cpptools.h b/src/plugins/cpptools/cpptools.h new file mode 100644 index 0000000000..3d0f195309 --- /dev/null +++ b/src/plugins/cpptools/cpptools.h @@ -0,0 +1,86 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLS_H +#define CPPTOOLS_H + +#include <extensionsystem/iplugin.h> +#include <projectexplorer/ProjectExplorerInterfaces> + +QT_BEGIN_NAMESPACE +class QFileInfo; +class QDir; +QT_END_NAMESPACE + +namespace Core { +class ICore; +} + +namespace CppTools { +namespace Internal { + +class CppCodeCompletion; +class CppModelManager; + +class CppToolsPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + static CppToolsPlugin *instance() { return m_instance; } + + CppToolsPlugin(); + ~CppToolsPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + CppModelManager *cppModelManager() { return m_modelManager; } + QString correspondingHeaderOrSource(const QString &fileName) const; + +private slots: + void switchHeaderSource(); + +private: + QString correspondingHeaderOrSourceI(const QString &fileName) const; + QFileInfo findFile(const QDir &dir, const QString &name, const ProjectExplorer::Project *project) const; + + Core::ICore *m_core; + int m_context; + CppModelManager *m_modelManager; + + static CppToolsPlugin *m_instance; +}; + +} // namespace Internal +} // namespace CppTools + +#endif // CPPTOOLS_H diff --git a/src/plugins/cpptools/cpptools.pri b/src/plugins/cpptools/cpptools.pri new file mode 100644 index 0000000000..1dffbfc556 --- /dev/null +++ b/src/plugins/cpptools/cpptools.pri @@ -0,0 +1,3 @@ +include(cpptools_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(CppTools) diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro new file mode 100644 index 0000000000..17b72496a9 --- /dev/null +++ b/src/plugins/cpptools/cpptools.pro @@ -0,0 +1,40 @@ +TEMPLATE = lib +TARGET = CppTools +include(../../qworkbenchplugin.pri) +include(../../plugins/quickopen/quickopen.pri) +include(cpptools_dependencies.pri) + +#DEFINES += QT_NO_CAST_FROM_ASCII +DEFINES += QT_NO_CAST_TO_ASCII +unix:QMAKE_CXXFLAGS_DEBUG+=-O3 + +INCLUDEPATH += . + +DEFINES += CPPTOOLS_LIBRARY + +CONFIG += help +include(rpp/rpp.pri)|error("Can't find RPP") + +HEADERS += \ + cpptools_global.h \ + cppquickopenfilter.h + +SOURCES += \ + cppquickopenfilter.cpp \ + cpptoolseditorsupport.cpp + +# Input +SOURCES += cpptools.cpp \ + cppmodelmanager.cpp \ + cppcodecompletion.cpp \ + cpphoverhandler.cpp + +HEADERS += cpptools.h \ + cppmodelmanager.h \ + cppcodecompletion.h \ + cpphoverhandler.h \ + cppmodelmanagerinterface.h \ + cpptoolseditorsupport.h \ + cpptoolsconstants.h + +RESOURCES += cpptools.qrc diff --git a/src/plugins/cpptools/cpptools.qrc b/src/plugins/cpptools/cpptools.qrc new file mode 100644 index 0000000000..a750578a4b --- /dev/null +++ b/src/plugins/cpptools/cpptools.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/cpptools" > + <file>images/f1.svg</file> + </qresource> +</RCC> diff --git a/src/plugins/cpptools/cpptools_dependencies.pri b/src/plugins/cpptools/cpptools_dependencies.pri new file mode 100644 index 0000000000..e12a33bc46 --- /dev/null +++ b/src/plugins/cpptools/cpptools_dependencies.pri @@ -0,0 +1,3 @@ +include(../../libs/cplusplus/cplusplus.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) diff --git a/src/plugins/cpptools/cpptools_global.h b/src/plugins/cpptools/cpptools_global.h new file mode 100644 index 0000000000..294a54ceb2 --- /dev/null +++ b/src/plugins/cpptools/cpptools_global.h @@ -0,0 +1,42 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLS_GLOBAL_H +#define CPPTOOLS_GLOBAL_H + +#if defined(CPPTOOLS_LIBRARY) +# define CPPTOOLS_EXPORT Q_DECL_EXPORT +#else +# define CPPTOOLS_EXPORT Q_DECL_IMPORT +#endif + +#endif // CPPTOOLS_GLOBAL_H diff --git a/src/plugins/cpptools/cpptoolsconstants.h b/src/plugins/cpptools/cpptoolsconstants.h new file mode 100644 index 0000000000..8a3e92cf2d --- /dev/null +++ b/src/plugins/cpptools/cpptoolsconstants.h @@ -0,0 +1,49 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLSCONSTANTS_H +#define CPPTOOLSCONSTANTS_H + +namespace CppTools { +namespace Constants { + +const char * const M_TOOLS_CPP = "CppTools.Tools.Menu"; +const char * const SWITCH_HEADER_SOURCE = "CppTools.SwitchHeaderSource"; +const char * const TASK_INDEX = "CppTools.Task.Index"; +const char * const C_SOURCE_MIMETYPE = "text/x-csrc"; +const char * const C_HEADER_MIMETYPE = "text/x-chdr"; +const char * const CPP_SOURCE_MIMETYPE = "text/x-c++src"; +const char * const CPP_HEADER_MIMETYPE = "text/x-c++hdr"; +} +} + +#endif //CPPTOOLSCONSTANTS_H diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp new file mode 100644 index 0000000000..ca36045422 --- /dev/null +++ b/src/plugins/cpptools/cpptoolseditorsupport.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "cpptoolseditorsupport.h" +#include "cppmodelmanager.h" + +#include <texteditor/itexteditor.h> +#include <QTimer> + +using namespace CppTools::Internal; + +CppEditorSupport::CppEditorSupport(CppModelManager *modelManager) + : QObject(modelManager), + _modelManager(modelManager), + _updateDocumentInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL) +{ + _updateDocumentTimer = new QTimer(this); + _updateDocumentTimer->setSingleShot(true); + _updateDocumentTimer->setInterval(_updateDocumentInterval); + connect(_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow())); +} + +CppEditorSupport::~CppEditorSupport() +{ } + +TextEditor::ITextEditor *CppEditorSupport::textEditor() const +{ return _textEditor; } + +void CppEditorSupport::setTextEditor(TextEditor::ITextEditor *textEditor) +{ + _textEditor = textEditor; + + if (! _textEditor) + return; + + connect(_textEditor, SIGNAL(contentsChanged()), this, SLOT(updateDocument())); + updateDocument(); +} + +QString CppEditorSupport::contents() const +{ + if (! _textEditor) + return QString(); + + return _textEditor->contents(); +} + +int CppEditorSupport::updateDocumentInterval() const +{ return _updateDocumentInterval; } + +void CppEditorSupport::setUpdateDocumentInterval(int updateDocumentInterval) +{ _updateDocumentInterval = updateDocumentInterval; } + +void CppEditorSupport::updateDocument() +{ _updateDocumentTimer->start(_updateDocumentInterval); } + +void CppEditorSupport::updateDocumentNow() +{ + if (_documentParser.isRunning()) { + _updateDocumentTimer->start(_updateDocumentInterval); + } else { + _updateDocumentTimer->stop(); + QStringList sourceFiles(_textEditor->file()->fileName()); + _documentParser = _modelManager->refreshSourceFiles(sourceFiles); + } +} + diff --git a/src/plugins/cpptools/cpptoolseditorsupport.h b/src/plugins/cpptools/cpptoolseditorsupport.h new file mode 100644 index 0000000000..51a905e895 --- /dev/null +++ b/src/plugins/cpptools/cpptoolseditorsupport.h @@ -0,0 +1,87 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CPPTOOLSEDITORSUPPORT_H +#define CPPTOOLSEDITORSUPPORT_H + +#include <QObject> +#include <QPointer> +#include <QFuture> + +QT_BEGIN_NAMESPACE +class QTimer; +class QByteArray; +QT_END_NAMESPACE + +namespace TextEditor { + class ITextEditor; +} // end of namespace TextEditor + +namespace CppTools { +namespace Internal { + +class CppModelManager; + +class CppEditorSupport: public QObject +{ + Q_OBJECT + +public: + CppEditorSupport(CppModelManager *modelManager); + virtual ~CppEditorSupport(); + + TextEditor::ITextEditor *textEditor() const; + void setTextEditor(TextEditor::ITextEditor *textEditor); + + int updateDocumentInterval() const; + void setUpdateDocumentInterval(int updateDocumentInterval); + + QString contents() const; + +private Q_SLOTS: + void updateDocument(); + void updateDocumentNow(); + +private: + enum { UPDATE_DOCUMENT_DEFAULT_INTERVAL = 150 }; + + CppModelManager *_modelManager; + QPointer<TextEditor::ITextEditor> _textEditor; + QTimer *_updateDocumentTimer; + int _updateDocumentInterval; + QFuture<void> _documentParser; +}; + +} // end of namespace Internal +} // end of namespace CppTools + +#endif // CPPTOOLSEDITORSUPPORT_H diff --git a/src/plugins/cpptools/images/f1.svg b/src/plugins/cpptools/images/f1.svg new file mode 100644 index 0000000000..468594cb77 --- /dev/null +++ b/src/plugins/cpptools/images/f1.svg @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="24" + height="24" + id="svg2411" + sodipodi:version="0.32" + inkscape:version="0.46" + version="1.0" + sodipodi:docname="f1.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs2413"> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient001" + id="linearGradient3201" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.6285394,0,0,3.6290105,-1258.7023,-359.38242)" + spreadMethod="pad" + x1="375.31006" + y1="88.869247" + x2="466.8873" + y2="180.4346" /> + <linearGradient + id="linearGradient001"> + <stop + id="stop608" + offset="0.000000" + style="stop-color:#cfcfcf;stop-opacity:1;" /> + <stop + id="stop609" + offset="1.000000" + style="stop-color:#efefef;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient001" + id="linearGradient3207" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.8401167,0,0,3.8424815,-1348.031,-388.46373)" + spreadMethod="pad" + x1="470.3931" + y1="136.23064" + x2="374.90988" + y2="136.23064" /> + <linearGradient + id="linearGradient002"> + <stop + id="stop566" + offset="0.000000" + style="stop-color:#9d9d9f;stop-opacity:1;" /> + <stop + id="stop567" + offset="1.000000" + style="stop-color:#e5e5e5;stop-opacity:1;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient002" + id="linearGradient2419" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(3.6611924,0,0,3.6628816,-1231.7325,-383.72165)" + spreadMethod="pad" + x1="471.00525" + y1="201.05208" + x2="348.94803" + y2="79.051147" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective2419" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="11.2" + inkscape:cx="22.801892" + inkscape:cy="10.456883" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="963" + inkscape:window-height="667" + inkscape:window-x="207" + inkscape:window-y="207" /> + <metadata + id="metadata2416"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Ebene 1" + inkscape:groupmode="layer" + id="layer1"> + <g + transform="matrix(4.3636364e-2,0,0,4.3636364e-2,0,6.1090908)" + id="g2404"> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + y="-140" + x="2.4832567e-14" + width="550" + style="font-size:12px;fill:#b0b0b0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.81658993pt;stroke-opacity:1" + ry="81.511414" + id="rect621" + height="550" /> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + y="-128.55069" + x="11.458333" + width="527.08331" + style="font-size:12px;fill:url(#linearGradient2419);fill-rule:evenodd;stroke:none;stroke-width:1.03125;stroke-miterlimit:4;stroke-dasharray:none" + ry="78.116447" + id="rect2417" + height="527.09235" /> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + y="-71.25" + x="68.950378" + width="412.29962" + style="font-size:12px;fill:url(#linearGradient3207);fill-rule:evenodd;stroke:none;stroke-width:2.4979167;stroke-miterlimit:4;stroke-dasharray:none" + ry="39.785442" + rx="34.514793" + id="rect3205" + height="412.5" /> + <rect + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + y="-59.791668" + x="80.208336" + width="389.58334" + style="font-size:12px;fill:url(#linearGradient3201);fill-rule:evenodd;stroke:none;stroke-width:25.41458321;stroke-miterlimit:4;stroke-dasharray:none" + ry="25.400656" + rx="22.902761" + id="rect3199" + height="389.58334" /> + <path + sodipodi:nodetypes="ccccccccccccccccsccc" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="c:\Documents and Settings\aportale\Desktop\rect621.png" + id="text3219" + d="M 137.5,157.91667 L 137.5,-2.4752517 L 229.16667,-2.5 L 229.16667,20.416667 L 160.41667,20.416667 L 160.41667,66.25 L 206.25,66.25 L 206.25,89.166667 L 160.41667,89.166667 L 160.41667,157.91667 L 137.5,157.91667 z M 320.89291,157.91667 L 297.91667,157.91667 L 297.91667,40.04211 C 284.08134,48.574912 275.21133,59.2993 252.08333,66.25 L 252.08333,47.355939 C 263.04835,44.807707 286.00818,25.67917 293.48823,18.312241 C 300.9682,10.945593 306.26379,4.4293192 309.375,-2.5 L 320.83333,-2.5 L 320.89291,157.91667 z" + style="font-size:261.65481567px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans" /> + </g> + </g> +</svg> diff --git a/src/plugins/cpptools/rpp/pp-cctype.h b/src/plugins/cpptools/rpp/pp-cctype.h new file mode 100644 index 0000000000..aff4fb2c51 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-cctype.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_CCTYPE_H +#define PP_CCTYPE_H + +#include <cctype> + +namespace rpp { + +inline bool pp_isalpha (int __ch) +{ return std::isalpha ((unsigned char) __ch) != 0; } + +inline bool pp_isalnum (int __ch) +{ return std::isalnum ((unsigned char) __ch) != 0; } + +inline bool pp_isdigit (int __ch) +{ return std::isdigit ((unsigned char) __ch) != 0; } + +inline bool pp_isspace (int __ch) +{ return std::isspace ((unsigned char) __ch) != 0; } + +} // namespace rpp + +#endif // PP_CCTYPE_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/src/plugins/cpptools/rpp/pp-client.h b/src/plugins/cpptools/rpp/pp-client.h new file mode 100644 index 0000000000..13d9eda713 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-client.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef PP_CLIENT_H +#define PP_CLIENT_H + +#include <QByteArray> +#include <QString> +#include <QFile> + +namespace rpp { + +class Client +{ + Client(const Client &other); + void operator=(const Client &other); + +public: + enum IncludeType { + IncludeLocal, + IncludeGlobal + }; + +public: + Client() + { } + + virtual ~Client() + { } + + virtual void macroAdded(const QByteArray ¯oId, const QByteArray &text) = 0; + virtual void sourceNeeded(QString &fileName, IncludeType mode) = 0; // ### FIX the signature. + + virtual void startSkippingBlocks(unsigned offset) = 0; + virtual void stopSkippingBlocks(unsigned offset) = 0; +}; + +} // end of namespace rpp + +#endif // PP_CLIENT_H diff --git a/src/plugins/cpptools/rpp/pp-engine.cpp b/src/plugins/cpptools/rpp/pp-engine.cpp new file mode 100644 index 0000000000..97e168f0c2 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-engine.cpp @@ -0,0 +1,1085 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "pp.h" +#include <Lexer.h> +#include <Token.h> +#include <QtDebug> + +using namespace rpp; +using namespace CPlusPlus; + +namespace { + +class RangeLexer +{ + const Token *first; + const Token *last; + Token trivial; + +public: + inline RangeLexer(const Token *first, const Token *last) + : first(first), last(last) + { + // WARN: `last' must be a valid iterator. + trivial.offset = last->offset; + } + + inline operator bool() const + { return first != last; } + + inline bool isValid() const + { return first != last; } + + inline int size() const + { return std::distance(first, last); } + + inline const Token *dot() const + { return first; } + + inline const Token &operator*() const + { + if (first != last) + return *first; + + return trivial; + } + + inline const Token *operator->() const + { + if (first != last) + return first; + + return &trivial; + } + + inline RangeLexer &operator++() + { + ++first; + return *this; + } +}; + +class ExpressionEvaluator +{ + ExpressionEvaluator(const ExpressionEvaluator &other); + void operator = (const ExpressionEvaluator &other); + +public: + ExpressionEvaluator(Environment *env) + : env(env), _lex(0) + { } + + Value operator()(const Token *firstToken, const Token *lastToken, + const QByteArray &source) + { + this->source = source; + const Value previousValue = switchValue(Value()); + RangeLexer tmp(firstToken, lastToken); + RangeLexer *previousLex = _lex; + _lex = &tmp; + process_expression(); + _lex = previousLex; + return switchValue(previousValue); + } + +protected: + Value switchValue(const Value &value) + { + Value previousValue = _value; + _value = value; + return previousValue; + } + + bool isTokenDefined() const + { + if ((*_lex)->isNot(T_IDENTIFIER)) + return false; + const QByteArray spell = tokenSpell(); + if (spell.size() != 7) + return false; + return spell == "defined"; + } + + QByteArray tokenSpell() const + { + const QByteArray text = QByteArray::fromRawData(source.constData() + (*_lex)->offset, + (*_lex)->length); + return text; + } + + bool process_expression() + { return process_constant_expression(); } + + bool process_primary() + { + if ((*_lex)->is(T_INT_LITERAL)) { + _value.set_long(tokenSpell().toLong()); + ++(*_lex); + return true; + } else if (isTokenDefined()) { + ++(*_lex); + if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(env->resolve(tokenSpell()) != 0); + ++(*_lex); + return true; + } else if ((*_lex)->is(T_LPAREN)) { + ++(*_lex); + if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(env->resolve(tokenSpell()) != 0); + ++(*_lex); + if ((*_lex)->is(T_RPAREN)) { + ++(*_lex); + return true; + } + } + return false; + } + return true; + } else if ((*_lex)->is(T_IDENTIFIER)) { + _value.set_long(0); + ++(*_lex); + return true; + } else if ((*_lex)->is(T_MINUS)) { + ++(*_lex); + process_primary(); + _value.set_long(- _value.l); + return true; + } else if ((*_lex)->is(T_PLUS)) { + ++(*_lex); + process_primary(); + return true; + } else if ((*_lex)->is(T_EXCLAIM)) { + ++(*_lex); + process_primary(); + _value.set_long(_value.is_zero()); + return true; + } else if ((*_lex)->is(T_LPAREN)) { + ++(*_lex); + process_expression(); + if ((*_lex)->is(T_RPAREN)) + ++(*_lex); + return true; + } + + return false; + } + + bool process_multiplicative() + { + process_primary(); + + while ((*_lex)->is(T_STAR) || (*_lex)->is(T_SLASH) || (*_lex)->is(T_PERCENT)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_primary(); + + if (op.is(T_STAR)) { + _value = left * _value; + } else if (op.is(T_SLASH)) { + if (_value.is_zero()) + _value.set_long(0); + else + _value = left / _value; + } else if (op.is(T_PERCENT)) { + if (_value.is_zero()) + _value.set_long(0); + else + _value = left % _value; + } + } + + return true; + } + + bool process_additive() + { + process_multiplicative(); + + while ((*_lex)->is(T_PLUS) || (*_lex)->is(T_MINUS)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_multiplicative(); + + if (op.is(T_PLUS)) + _value = left + _value; + else if (op.is(T_MINUS)) + _value = left - _value; + } + + return true; + } + + bool process_shift() + { + process_additive(); + + while ((*_lex)->is(T_MINUS_MINUS) || (*_lex)->is(T_GREATER_GREATER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_additive(); + + if (op.is(T_MINUS_MINUS)) + _value = left << _value; + else if (op.is(T_GREATER_GREATER)) + _value = left >> _value; + } + + return true; + } + + bool process_relational() + { + process_shift(); + + while ((*_lex)->is(T_LESS) || (*_lex)->is(T_LESS_EQUAL) || + (*_lex)->is(T_GREATER) || (*_lex)->is(T_GREATER_EQUAL)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_shift(); + + if (op.is(T_LESS)) + _value = left < _value; + else if (op.is(T_LESS_EQUAL)) + _value = left <= _value; + else if (op.is(T_GREATER)) + _value = left > _value; + else if (op.is(T_GREATER_EQUAL)) + _value = left >= _value; + } + + return true; + } + + bool process_equality() + { + process_relational(); + + while ((*_lex)->is(T_EXCLAIM_EQUAL) || (*_lex)->is(T_EQUAL_EQUAL)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_relational(); + + if (op.is(T_EXCLAIM_EQUAL)) + _value = left != _value; + else if (op.is(T_EQUAL_EQUAL)) + _value = left == _value; + } + + return true; + } + + bool process_and() + { + process_equality(); + + while ((*_lex)->is(T_AMPER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_equality(); + + _value = left & _value; + } + + return true; + } + + bool process_xor() + { + process_and(); + + while ((*_lex)->is(T_CARET)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_and(); + + _value = left ^ _value; + } + + return true; + } + + bool process_or() + { + process_xor(); + + while ((*_lex)->is(T_CARET)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_xor(); + + _value = left | _value; + } + + return true; + } + + bool process_logical_and() + { + process_or(); + + while ((*_lex)->is(T_AMPER_AMPER)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_or(); + + _value = left && _value; + } + + return true; + } + + bool process_logical_or() + { + process_logical_and(); + + while ((*_lex)->is(T_PIPE_PIPE)) { + const Token op = *(*_lex); + ++(*_lex); + + const Value left = _value; + process_logical_and(); + + _value = left || _value; + } + + return true; + } + + bool process_constant_expression() + { + process_logical_or(); + const Value cond = _value; + if ((*_lex)->is(T_QUESTION)) { + ++(*_lex); + process_constant_expression(); + Value left = _value, right; + if ((*_lex)->is(T_COLON)) { + ++(*_lex); + process_constant_expression(); + right = _value; + } + _value = ! cond.is_zero() ? left : right; + } + + return true; + } + +private: + Environment *env; + QByteArray source; + RangeLexer *_lex; + Value _value; +}; + +} // end of anonymous namespace + + +pp::pp (Client *client, Environment &env) + : client(client), + env(env), + expand(env) +{ + resetIfLevel (); +} + +void pp::pushState(const State &s) +{ + _savedStates.append(state()); + _source = s.source; + _tokens = s.tokens; + _dot = s.dot; +} + +pp::State pp::state() const +{ + State state; + state.source = _source; + state.tokens = _tokens; + state.dot = _dot; + return state; +} + +void pp::popState() +{ + const State &state = _savedStates.last(); + _source = state.source; + _tokens = state.tokens; + _dot = state.dot; + _savedStates.removeLast(); +} + +void pp::operator () (const QByteArray &filename, + const QByteArray &source, + QByteArray *result) +{ + const QByteArray previousFile = env.current_file; + env.current_file = filename; + + operator () (source, result); + + env.current_file = previousFile; +} + +pp::State pp::createStateFromSource(const QByteArray &source) const +{ + State state; + state.source = source; + Lexer lex(state.source.constBegin(), state.source.constEnd()); + lex.setScanKeywords(false); + Token tok; + do { + lex(&tok); + state.tokens.append(tok); + } while (tok.isNot(T_EOF_SYMBOL)); + state.dot = state.tokens.constBegin(); + return state; +} + +void pp::operator()(const QByteArray &source, QByteArray *result) +{ + pushState(createStateFromSource(source)); + + const unsigned previousCurrentLine = env.currentLine; + env.currentLine = 0; + + while (true) { + if (env.currentLine != _dot->lineno) { + if (env.currentLine > _dot->lineno) { + result->append('\n'); + result->append('#'); + result->append(QByteArray::number(_dot->lineno)); + result->append(' '); + result->append('"'); + result->append(env.current_file); + result->append('"'); + result->append('\n'); + } else { + for (unsigned i = env.currentLine; i < _dot->lineno; ++i) + result->append('\n'); + } + env.currentLine = _dot->lineno; + } + + if (_dot->is(T_EOF_SYMBOL)) { + break; + } else if (_dot->is(T_POUND) && (! _dot->joined && _dot->newline)) { + TokenIterator start = _dot; + do { + ++_dot; + } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->joined || ! _dot->newline)); + + //qDebug() << QByteArray(first + beginPP.offset, + //tokens.last().end() - beginPP.offset); + + const bool skippingBlocks = _skipping[iflevel]; + + processDirective(start, _dot); + + if (client && skippingBlocks != _skipping[iflevel]) { + unsigned offset = start->offset; + if (_skipping[iflevel]) { + if (_dot->newline) + ++offset; + client->startSkippingBlocks(offset); + } else { + if (offset) + --offset; + client->stopSkippingBlocks(offset); + } + } + } else if (skipping()) { + do { + ++_dot; + } while (_dot->isNot(T_EOF_SYMBOL) && (_dot->joined || ! _dot->newline)); + } else { + if (_dot->joined) + result->append("\\\n"); + else if (_dot->newline) { + result->append('\n'); + result->append('#'); + result->append(QByteArray::number(_dot->lineno)); + result->append(' '); + result->append('"'); + result->append(env.current_file); + result->append('"'); + result->append('\n'); + } + else if (_dot->whitespace) + result->append(' '); + + if (_dot->isNot(T_IDENTIFIER)) { + result->append(tokenSpell(*_dot)); + ++_dot; + } else { + const TokenIterator identifierToken = _dot; + ++_dot; // skip T_IDENTIFIER + + const QByteArray spell = tokenSpell(*identifierToken); + if (env.isBuiltinMacro(spell)) { + expand(spell.constBegin(), spell.constEnd(), result); + continue; + } + + Macro *m = env.resolve(spell); + if (! m) { + result->append(spell); + } else { + if (! m->function_like) { + if (_dot->isNot(T_LPAREN)) { + expand(m->definition.constBegin(), + m->definition.constEnd(), + result); + continue; + } else { + QByteArray tmp; + expand(m->definition.constBegin(), + m->definition.constEnd(), + &tmp); + + m = 0; // reset the active the macro + + pushState(createStateFromSource(tmp)); + if (_dot->is(T_IDENTIFIER)) { + const QByteArray id = tokenSpell(*_dot); + Macro *macro = env.resolve(id); + if (macro && macro->function_like) + m = macro; + } + popState(); + + if (! m) { + result->append(tmp); + continue; + } + } + } + + // collect the actual arguments + if (_dot->isNot(T_LPAREN)) { + // ### warnng expected T_LPAREN + result->append(m->name); + continue; + } + + int count = 0; + while (_dot->isNot(T_EOF_SYMBOL)) { + if (_dot->is(T_LPAREN)) + ++count; + else if (_dot->is(T_RPAREN)) { + if (! --count) + break; + } + ++_dot; + } + if (_dot->isNot(T_RPAREN)) { + // ### warning expected T_RPAREN + } else { + const char *beginOfText = startOfToken(*identifierToken); + const char *endOfText = endOfToken(*_dot); + ++_dot; // skip T_RPAREN + expand(beginOfText, endOfText, result); + } + } + } + } + } + + popState(); + env.currentLine = previousCurrentLine; +} + +const char *pp::startOfToken(const Token &token) const +{ return _source.constBegin() + token.begin(); } + +const char *pp::endOfToken(const Token &token) const +{ return _source.constBegin() + token.end(); } + +QByteArray pp::tokenSpell(const Token &token) const +{ + const QByteArray text = QByteArray::fromRawData(_source.constBegin() + token.offset, + token.length); + return text; +} + +QByteArray pp::tokenText(const Token &token) const +{ + const QByteArray text(_source.constBegin() + token.offset, + token.length); + return text; +} + +void pp::processDirective(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + + if (tk->is(T_IDENTIFIER)) { + const QByteArray directive = tokenSpell(*tk); + switch (PP_DIRECTIVE_TYPE d = classifyDirective(directive)) { + case PP_DEFINE: + if (! skipping()) + processDefine(firstToken, lastToken); + break; + + case PP_INCLUDE: + case PP_INCLUDE_NEXT: + if (! skipping()) + processInclude(d == PP_INCLUDE_NEXT, firstToken, lastToken); + break; + + case PP_UNDEF: + if (! skipping()) + processUndef(firstToken, lastToken); + break; + + case PP_ELIF: + processElif(firstToken, lastToken); + break; + + case PP_ELSE: + processElse(firstToken, lastToken); + break; + + case PP_ENDIF: + processEndif(firstToken, lastToken); + break; + + case PP_IF: + processIf(firstToken, lastToken); + break; + + case PP_IFDEF: + case PP_IFNDEF: + processIfdef(d == PP_IFNDEF, firstToken, lastToken); + break; + + default: + break; + } // switch + } +} + +QVector<Token> pp::tokenize(const QByteArray &text) const +{ + QVector<Token> tokens; + Lexer lex(text.constBegin(), text.constEnd()); + lex.setScanKeywords(false); + Token tk; + do { + lex(&tk); + tokens.append(tk); + } while (tk.isNot(T_EOF_SYMBOL)); + return tokens; +} + +void pp::processInclude(bool skipCurentPath, + TokenIterator firstToken, TokenIterator lastToken, + bool acceptMacros) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + ++tk; // skip `include|nclude_next' + + if (acceptMacros && tk->is(T_IDENTIFIER)) { +#if 0 + QByteArray name; + name.reserve(256); + MacroExpander expandInclude(env); + expandInclude(startOfToken(tokens.at(2)), + startOfToken(tokens.last()), + &name); + const QByteArray previousSource = switchSource(name); + //processInclude(skipCurentPath, tokenize(name), /*accept macros=*/ false); + (void) switchSource(previousSource); +#endif + } else if (tk->is(T_LESS)) { + TokenIterator start = tk.dot(); + for (; tk->isNot(T_EOF_SYMBOL); ++tk) { + if (tk->is(T_GREATER)) + break; + } + const char *beginOfPath = endOfToken(*start); + const char *endOfPath = startOfToken(*tk); + const QByteArray path = QByteArray::fromRawData(beginOfPath, + endOfPath - beginOfPath); + + QString fn = QString::fromUtf8(path.constData(), path.length()); + + if (client) + client->sourceNeeded(fn, Client::IncludeGlobal); + } else if (tk->is(T_ANGLE_STRING_LITERAL) || tk->is(T_STRING_LITERAL)) { + const QByteArray spell = tokenSpell(*tk); + const char *beginOfPath = spell.constBegin(); + const char *endOfPath = spell.constEnd(); + const char quote = *beginOfPath; + if (beginOfPath + 1 != endOfPath && ((quote == '"' && endOfPath[-1] == '"') || + (quote == '<' && endOfPath[-1] == '>'))) { + const QByteArray path = QByteArray::fromRawData(beginOfPath + 1, + spell.length() - 2); + QString fn = QString::fromUtf8(path.constData(), path.length()); + + if (client) + client->sourceNeeded(fn, Client::IncludeLocal); + } + } +} + +void pp::processDefine(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + if (tk.size() < 3) + return; // nothing to do + + ++tk; // skip T_POUND + ++tk; // skip T_DEFINE + + if (tk->isNot(T_IDENTIFIER)) { + // ### warning expected an `identifier' + return; + } + + Macro macro; + macro.name = tokenText(*tk); + ++tk; // skip T_IDENTIFIER + + if (tk->is(T_LPAREN) && ! tk->whitespace) { + // a function-like macro definition + macro.function_like = true; + + ++tk; // skip T_LPAREN + if (tk->is(T_IDENTIFIER)) { + macro.formals.append(tokenText(*tk)); + ++tk; // skip T_IDENTIFIER + while (tk->is(T_COMMA)) { + ++tk;// skip T_COMMA + if (tk->isNot(T_IDENTIFIER)) + break; + macro.formals.append(tokenText(*tk)); + ++tk; // skip T_IDENTIFIER + } + } + + if (tk->is(T_DOT_DOT_DOT)) { + macro.variadics = true; + ++tk; // skip T_DOT_DOT_DOT + } + + if (tk->isNot(T_RPAREN)) { + // ### warning expected `)' + return; + } + + ++tk; // skip T_RPAREN + } + + QByteArray macroId = macro.name; + const bool isQtWord = isQtReservedWord(macroId); + + if (macro.function_like) { + macroId += '('; + for (int i = 0; i < macro.formals.size(); ++i) { + if (i != 0) + macroId += ", "; + + const QByteArray formal = macro.formals.at(i); + macroId += formal; + } + macroId += ')'; + } + + if (isQtWord) + macro.definition = macroId; + else { + const char *startOfDefinition = startOfToken(*tk); + const char *endOfDefinition = startOfToken(*lastToken); + macro.definition.append(startOfDefinition, + endOfDefinition - startOfDefinition); + macro.definition.replace("\\\n", " "); + } + + env.bind(macro); + + QByteArray macroText; + macroText.reserve(64); + macroText += "#define "; + + macroText += macroId; + macroText += ' '; + macroText += macro.definition; + macroText += '\n'; + + client->macroAdded(macroId, macroText); +} + +void pp::processIf(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + ++tk; // skip T_POUND + ++tk; // skipt `if' + + if (testIfLevel()) { + const char *first = startOfToken(*tk); + const char *last = startOfToken(*lastToken); + + MacroExpander expandCondition (env); + QByteArray condition; + condition.reserve(256); + expandCondition(first, last, &condition); + + QVector<Token> tokens = tokenize(condition); + + const Value result = evalExpression(tokens.constBegin(), + tokens.constEnd() - 1, + condition); + + _true_test[iflevel] = ! result.is_zero (); + _skipping[iflevel] = result.is_zero (); + } +} + +void pp::processElse(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + if (iflevel == 0 && !skipping ()) { + // std::cerr << "*** WARNING #else without #if" << std::endl; + } else if (iflevel > 0 && _skipping[iflevel - 1]) { + _skipping[iflevel] = true; + } else { + _skipping[iflevel] = _true_test[iflevel]; + } +} + +void pp::processElif(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + ++tk; // skip T_POUND + ++tk; // skipt `elif' + + if (! (iflevel > 0)) { + // std::cerr << "*** WARNING: " << __FILE__ << __LINE__ << std::endl; + } else if (iflevel == 0 && !skipping()) { + // std::cerr << "*** WARNING #else without #if" << std::endl; + } else if (!_true_test[iflevel] && !_skipping[iflevel - 1]) { + const Value result = evalExpression(tk.dot(), lastToken, _source); + _true_test[iflevel] = ! result.is_zero (); + _skipping[iflevel] = result.is_zero (); + } else { + _skipping[iflevel] = true; + } +} + +void pp::processEndif(TokenIterator, TokenIterator) +{ + if (iflevel == 0 && !skipping()) { + // std::cerr << "*** WARNING #endif without #if" << std::endl; + } else { + _skipping[iflevel] = false; + _true_test[iflevel] = false; + + --iflevel; + } +} + +void pp::processIfdef(bool checkUndefined, + TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + ++tk; // skip T_POUND + ++tk; // skip `ifdef' + if (testIfLevel()) { + if (tk->is(T_IDENTIFIER)) { + const QByteArray macroName = tokenSpell(*tk); + bool value = env.resolve(macroName) != 0 || env.isBuiltinMacro(macroName); + + if (checkUndefined) + value = ! value; + + _true_test[iflevel] = value; + _skipping [iflevel] = ! value; + } + } +} + +void pp::processUndef(TokenIterator firstToken, TokenIterator lastToken) +{ + RangeLexer tk(firstToken, lastToken); + + ++tk; // skip T_POUND + ++tk; // skip `undef' + + if (tk->is(T_IDENTIFIER)) { + const QByteArray macroName = tokenText(*tk); + env.remove(macroName); + + QByteArray macroText; + macroText += "#undef "; + macroText += macroName; + macroText += '\n'; + client->macroAdded(macroName, macroText); + } +} + +void pp::resetIfLevel () +{ + iflevel = 0; + _skipping[iflevel] = false; + _true_test[iflevel] = false; +} + +pp::PP_DIRECTIVE_TYPE pp::classifyDirective (const QByteArray &__directive) const +{ + switch (__directive.size()) + { + case 2: + if (__directive[0] == 'i' && __directive[1] == 'f') + return PP_IF; + break; + + case 4: + if (__directive[0] == 'e' && __directive == "elif") + return PP_ELIF; + else if (__directive[0] == 'e' && __directive == "else") + return PP_ELSE; + break; + + case 5: + if (__directive[0] == 'i' && __directive == "ifdef") + return PP_IFDEF; + else if (__directive[0] == 'u' && __directive == "undef") + return PP_UNDEF; + else if (__directive[0] == 'e' && __directive == "endif") + return PP_ENDIF; + break; + + case 6: + if (__directive[0] == 'i' && __directive == "ifndef") + return PP_IFNDEF; + else if (__directive[0] == 'd' && __directive == "define") + return PP_DEFINE; + break; + + case 7: + if (__directive[0] == 'i' && __directive == "include") + return PP_INCLUDE; + break; + + case 12: + if (__directive[0] == 'i' && __directive == "include_next") + return PP_INCLUDE_NEXT; + break; + + default: + break; + } + + return PP_UNKNOWN_DIRECTIVE; +} + +bool pp::testIfLevel() +{ + const bool result = !_skipping[iflevel++]; + _skipping[iflevel] = _skipping[iflevel - 1]; + _true_test[iflevel] = false; + return result; +} + +int pp::skipping() const +{ return _skipping[iflevel]; } + +Value pp::evalExpression(TokenIterator firstToken, TokenIterator lastToken, + const QByteArray &source) const +{ + ExpressionEvaluator eval(&env); + const Value result = eval(firstToken, lastToken, source); + return result; +} + +bool pp::isQtReservedWord (const QByteArray ¯oId) const +{ + const int size = macroId.size(); + if (size == 9 && macroId.at(0) == 'Q' && macroId == "Q_SIGNALS") + return true; + else if (size == 7 && macroId.at(0) == 'Q' && macroId == "Q_SLOTS") + return true; + else if (size == 6 && macroId.at(0) == 'S' && macroId == "SIGNAL") + return true; + else if (size == 4 && macroId.at(0) == 'S' && macroId == "SLOT") + return true; + else if (size == 7 && macroId.at(0) == 's' && macroId == "signals") + return true; + else if (size == 5 && macroId.at(0) == 's' && macroId == "slots") + return true; + return false; +} diff --git a/src/plugins/cpptools/rpp/pp-engine.h b/src/plugins/cpptools/rpp/pp-engine.h new file mode 100644 index 0000000000..fb3b9a3212 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-engine.h @@ -0,0 +1,230 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_ENGINE_H +#define PP_ENGINE_H + +#include "pp-client.h" +#include <Token.h> +#include <QVector> + +namespace CPlusPlus { + class Token; +} + +namespace rpp { + + struct Value + { + enum Kind { + Kind_Long, + Kind_ULong, + }; + + Kind kind; + + union { + long l; + unsigned long ul; + }; + + + Value() + : kind(Kind_Long), l(0) + { } + + inline bool is_ulong () const + { return kind == Kind_ULong; } + + inline void set_ulong (unsigned long v) + { + ul = v; + kind = Kind_ULong; + } + + inline void set_long (long v) + { + l = v; + kind = Kind_Long; + } + + inline bool is_zero () const + { return l == 0; } + +#define PP_DEFINE_BIN_OP(name, op) \ + inline Value operator op(const Value &other) const \ + { \ + Value v = *this; \ + if (v.is_ulong () || other.is_ulong ()) \ + v.set_ulong (v.ul op other.ul); \ + else \ + v.set_long (v.l op other.l); \ + return v; \ + } + + PP_DEFINE_BIN_OP(op_add, +) + PP_DEFINE_BIN_OP(op_sub, -) + PP_DEFINE_BIN_OP(op_mult, *) + PP_DEFINE_BIN_OP(op_div, /) + PP_DEFINE_BIN_OP(op_mod, %) + PP_DEFINE_BIN_OP(op_lhs, <<) + PP_DEFINE_BIN_OP(op_rhs, >>) + PP_DEFINE_BIN_OP(op_lt, <) + PP_DEFINE_BIN_OP(op_gt, >) + PP_DEFINE_BIN_OP(op_le, <=) + PP_DEFINE_BIN_OP(op_ge, >=) + PP_DEFINE_BIN_OP(op_eq, ==) + PP_DEFINE_BIN_OP(op_ne, !=) + PP_DEFINE_BIN_OP(op_bit_and, &) + PP_DEFINE_BIN_OP(op_bit_or, |) + PP_DEFINE_BIN_OP(op_bit_xor, ^) + PP_DEFINE_BIN_OP(op_and, &&) + PP_DEFINE_BIN_OP(op_or, ||) + +#undef PP_DEFINE_BIN_OP + }; + + class pp + { + Client *client; + Environment &env; + MacroExpander expand; + + enum { MAX_LEVEL = 512 }; + + bool _skipping[MAX_LEVEL]; // ### move in state + bool _true_test[MAX_LEVEL]; // ### move in state + int iflevel; // ### move in state + + enum PP_DIRECTIVE_TYPE + { + PP_UNKNOWN_DIRECTIVE, + PP_DEFINE, + PP_INCLUDE, + PP_INCLUDE_NEXT, + PP_ELIF, + PP_ELSE, + PP_ENDIF, + PP_IF, + PP_IFDEF, + PP_IFNDEF, + PP_UNDEF + }; + + typedef const CPlusPlus::Token *TokenIterator; + + struct State { + QByteArray source; + QVector<CPlusPlus::Token> tokens; + TokenIterator dot; + }; + + QList<State> _savedStates; + + State state() const; + void pushState(const State &state); + void popState(); + + QByteArray _source; + QVector<CPlusPlus::Token> _tokens; + TokenIterator _dot; + + State createStateFromSource(const QByteArray &source) const; + + public: + pp(Client *client, Environment &env); + + void operator()(const QByteArray &filename, + const QByteArray &source, + QByteArray *result); + + void operator()(const QByteArray &source, + QByteArray *result); + + private: + void resetIfLevel(); + bool testIfLevel(); + int skipping() const; + + PP_DIRECTIVE_TYPE classifyDirective(const QByteArray &directive) const; + + Value evalExpression(TokenIterator firstToken, + TokenIterator lastToken, + const QByteArray &source) const; + + QVector<CPlusPlus::Token> tokenize(const QByteArray &text) const; + + const char *startOfToken(const CPlusPlus::Token &token) const; + const char *endOfToken(const CPlusPlus::Token &token) const; + + QByteArray tokenSpell(const CPlusPlus::Token &token) const; + QByteArray tokenText(const CPlusPlus::Token &token) const; // does a deep copy + + void processDirective(TokenIterator dot, TokenIterator lastToken); + void processInclude(bool skipCurrentPath, + TokenIterator dot, TokenIterator lastToken, + bool acceptMacros = true); + void processDefine(TokenIterator dot, TokenIterator lastToken); + void processIf(TokenIterator dot, TokenIterator lastToken); + void processElse(TokenIterator dot, TokenIterator lastToken); + void processElif(TokenIterator dot, TokenIterator lastToken); + void processEndif(TokenIterator dot, TokenIterator lastToken); + void processIfdef(bool checkUndefined, + TokenIterator dot, TokenIterator lastToken); + void processUndef(TokenIterator dot, TokenIterator lastToken); + + bool isQtReservedWord(const QByteArray &name) const; + }; + +} // namespace rpp + +#endif // PP_ENGINE_H diff --git a/src/plugins/cpptools/rpp/pp-environment.cpp b/src/plugins/cpptools/rpp/pp-environment.cpp new file mode 100644 index 0000000000..1503787898 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-environment.cpp @@ -0,0 +1,231 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "pp-environment.h" +#include "pp.h" +#include <cstring> + +using namespace rpp; + +Environment::Environment () + : currentLine(0), + hide_next(false), + _macros(0), + _allocated_macros(0), + _macro_count(-1), + _hash(0), + _hash_count(401) +{ +} + +Environment::~Environment () +{ + if (_macros) { + qDeleteAll(firstMacro(), lastMacro()); + free(_macros); + } + + if (_hash) + free(_hash); +} + +unsigned Environment::macroCount () const +{ return _macro_count + 1; } + +Macro *Environment::macroAt (unsigned index) const +{ return _macros[index]; } + +Macro *Environment::bind(const Macro &__macro) +{ + Q_ASSERT(! __macro.name.isEmpty()); + + Macro *m = new Macro (__macro); + m->hashcode = hash_code(m->name); + m->fileName = current_file; + m->line = currentLine; + + if (++_macro_count == _allocated_macros) { + if (! _allocated_macros) + _allocated_macros = 401; + else + _allocated_macros <<= 1; + + _macros = (Macro **) realloc(_macros, sizeof(Macro *) * _allocated_macros); + } + + _macros[_macro_count] = m; + + if (! _hash || _macro_count > (_hash_count >> 1)) { + rehash(); + } else { + const unsigned h = m->hashcode % _hash_count; + m->next = _hash[h]; + _hash[h] = m; + } + + return m; +} + +void Environment::remove (const QByteArray &name) +{ + Macro macro; + macro.name = name; + macro.hidden = true; + bind(macro); +} + +bool Environment::isBuiltinMacro(const QByteArray &s) const +{ + if (s.length() != 8) + return false; + + if (s[0] == '_') { + if (s[1] == '_') { + if (s[2] == 'D') { + if (s[3] == 'A') { + if (s[4] == 'T') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + else if (s[2] == 'F') { + if (s[3] == 'I') { + if (s[4] == 'L') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + else if (s[2] == 'L') { + if (s[3] == 'I') { + if (s[4] == 'N') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + else if (s[2] == 'T') { + if (s[3] == 'I') { + if (s[4] == 'M') { + if (s[5] == 'E') { + if (s[6] == '_') { + if (s[7] == '_') { + return true; + } + } + } + } + } + } + } + } + return false; +} + +Macro *Environment::resolve (const QByteArray &name) const +{ + if (! _macros) + return 0; + + Macro *it = _hash[hash_code (name) % _hash_count]; + for (; it; it = it->next) { + if (it->name != name) + continue; + else if (it->hidden) + return 0; + else break; + } + return it; +} + +unsigned Environment::hash_code (const QByteArray &s) +{ + unsigned hash_value = 0; + + for (int i = 0; i < s.size (); ++i) + hash_value = (hash_value << 5) - hash_value + s.at (i); + + return hash_value; +} + +void Environment::rehash() +{ + if (_hash) { + free(_hash); + _hash_count <<= 1; + } + + _hash = (Macro **) calloc(_hash_count, sizeof(Macro *)); + + for (Macro **it = firstMacro(); it != lastMacro(); ++it) { + Macro *m= *it; + const unsigned h = m->hashcode % _hash_count; + m->next = _hash[h]; + _hash[h] = m; + } +} diff --git a/src/plugins/cpptools/rpp/pp-environment.h b/src/plugins/cpptools/rpp/pp-environment.h new file mode 100644 index 0000000000..cdecf4de61 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-environment.h @@ -0,0 +1,109 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_ENVIRONMENT_H +#define PP_ENVIRONMENT_H + +#include <QVector> +#include <QByteArray> + +namespace rpp { + +struct Macro; + +class Environment +{ +public: + Environment(); + ~Environment(); + + unsigned macroCount() const; + Macro *macroAt(unsigned index) const; + + Macro *bind(const Macro ¯o); + void remove(const QByteArray &name); + + Macro *resolve(const QByteArray &name) const; + bool isBuiltinMacro(const QByteArray &name) const; + + const Macro *const *firstMacro() const + { return _macros; } + + Macro **firstMacro() + { return _macros; } + + const Macro *const *lastMacro() const + { return _macros + _macro_count + 1; } + + Macro **lastMacro() + { return _macros + _macro_count + 1; } + +private: + static unsigned hash_code (const QByteArray &s); + void rehash(); + +public: + QByteArray current_file; + unsigned currentLine; + bool hide_next; + +private: + Macro **_macros; + int _allocated_macros; + int _macro_count; + Macro **_hash; + int _hash_count; +}; + +} // namespace rpp + +#endif // PP_ENVIRONMENT_H diff --git a/src/plugins/cpptools/rpp/pp-fwd.h b/src/plugins/cpptools/rpp/pp-fwd.h new file mode 100644 index 0000000000..05b68774cf --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-fwd.h @@ -0,0 +1,62 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_FWD_H +#define PP_FWD_H + +namespace rpp { + +} // namespace rpp + +#endif // PP_FWD_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/src/plugins/cpptools/rpp/pp-internal.h b/src/plugins/cpptools/rpp/pp-internal.h new file mode 100644 index 0000000000..eed958372c --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-internal.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_INTERNAL_H +#define PP_INTERNAL_H + +#include <QByteArray> + +namespace rpp { + + namespace _PP_internal + { + + inline void output_line(const QByteArray &__filename, int __line, QByteArray *__result) + { + QByteArray __msg; + + __msg += "# "; + + char __line_descr[16]; + qsnprintf (__line_descr, 16, "%d", __line); + __msg += __line_descr; + + __msg += " \""; + + if (__filename.isEmpty ()) + __msg += "<editor>"; + else + __msg += __filename; + + __msg += "\"\n"; + __result->append(__msg); + } + + inline bool comment_p (const char *__first, const char *__last) + { + if (__first == __last) + return false; + + if (*__first != '/') + return false; + + if (++__first == __last) + return false; + + return (*__first == '/' || *__first == '*'); + } + + } // _PP_internal + +} // namespace rpp + +#endif // PP_INTERNAL_H diff --git a/src/plugins/cpptools/rpp/pp-macro-expander.cpp b/src/plugins/cpptools/rpp/pp-macro-expander.cpp new file mode 100644 index 0000000000..658f2acc67 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-macro-expander.cpp @@ -0,0 +1,362 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "pp.h" +#include "pp-macro-expander.h" +#include <QDateTime> + +using namespace rpp; + +MacroExpander::MacroExpander (Environment &env, pp_frame *frame) + : env (env), frame (frame), + lines (0), generated_lines (0) +{ } + +const QByteArray *MacroExpander::resolve_formal (const QByteArray &__name) +{ + if (! (frame && frame->expanding_macro)) + return 0; + + const QVector<QByteArray> &formals = frame->expanding_macro->formals; + for (int index = 0; index < formals.size(); ++index) { + const QByteArray formal = formals.at(index); + + if (formal == __name && index < frame->actuals.size()) + return &frame->actuals.at(index); + } + + return 0; +} + +const char *MacroExpander::operator () (const char *__first, const char *__last, + QByteArray *__result) +{ + generated_lines = 0; + __first = skip_blanks (__first, __last); + lines = skip_blanks.lines; + + while (__first != __last) + { + if (*__first == '\n') + { + __result->append('\n'); + __result->append('#'); + __result->append(QByteArray::number(env.currentLine)); + __result->append(' '); + __result->append('"'); + __result->append(env.current_file); + __result->append('"'); + __result->append('\n'); + ++lines; + + __first = skip_blanks (++__first, __last); + lines += skip_blanks.lines; + + if (__first != __last && *__first == '#') + break; + } + else if (*__first == '#') + { + __first = skip_blanks (++__first, __last); + lines += skip_blanks.lines; + + const char *end_id = skip_identifier (__first, __last); + const QByteArray fast_name(__first, end_id - __first); + __first = end_id; + + if (const QByteArray *actual = resolve_formal (fast_name)) + { + __result->append('\"'); + + const char *actual_begin = actual->constData (); + const char *actual_end = actual_begin + actual->size (); + + for (const char *it = skip_whitespaces (actual_begin, actual_end); + it != actual_end; ++it) + { + if (*it == '"' || *it == '\\') + { + __result->append('\\'); + __result->append(*it); + } + else if (*it == '\n') + { + __result->append('"'); + __result->append('\n'); + __result->append('"'); + } + else + __result->append(*it); + } + + __result->append('\"'); + } + else + __result->append('#'); // ### warning message? + } + else if (*__first == '\"') + { + const char *next_pos = skip_string_literal (__first, __last); + lines += skip_string_literal.lines; + __result->append(__first, next_pos - __first); + __first = next_pos; + } + else if (*__first == '\'') + { + const char *next_pos = skip_char_literal (__first, __last); + lines += skip_char_literal.lines; + __result->append(__first, next_pos - __first); + __first = next_pos; + } + else if (_PP_internal::comment_p (__first, __last)) + { + __first = skip_comment_or_divop (__first, __last); + int n = skip_comment_or_divop.lines; + lines += n; + + while (n-- > 0) + __result->append('\n'); + } + else if (pp_isspace (*__first)) + { + for (; __first != __last; ++__first) + { + if (*__first == '\n' || !pp_isspace (*__first)) + break; + } + + __result->append(' '); + } + else if (pp_isdigit (*__first)) + { + const char *next_pos = skip_number (__first, __last); + lines += skip_number.lines; + __result->append(__first, next_pos - __first); + __first = next_pos; + } + else if (pp_isalpha (*__first) || *__first == '_') + { + const char *name_begin = __first; + const char *name_end = skip_identifier (__first, __last); + __first = name_end; // advance + + // search for the paste token + const char *next = skip_blanks (__first, __last); + bool paste = false; + if (next != __last && *next == '#') + { + paste = true; + ++next; + if (next != __last && *next == '#') + __first = skip_blanks(++next, __last); + } + + const QByteArray fast_name(name_begin, name_end - name_begin); + + if (const QByteArray *actual = resolve_formal (fast_name)) + { + const char *begin = actual->constData (); + const char *end = begin + actual->size (); + if (paste) { + for (--end; end != begin - 1; --end) { + if (! pp_isspace(*end)) + break; + } + ++end; + } + __result->append(begin, end - begin); + continue; + } + + Macro *macro = env.resolve (fast_name); + if (! macro || macro->hidden || env.hide_next) + { + if (fast_name.size () == 7 && fast_name [0] == 'd' && fast_name == "defined") + env.hide_next = true; + else + env.hide_next = false; + + if (fast_name.size () == 8 && fast_name [0] == '_' && fast_name [1] == '_') + { + if (fast_name == "__LINE__") + { + char buf [16]; + const size_t count = qsnprintf (buf, 16, "%d", env.currentLine + lines); + __result->append(buf, count); + continue; + } + + else if (fast_name == "__FILE__") + { + __result->append('"'); + __result->append(env.current_file); + __result->append('"'); + continue; + } + + else if (fast_name == "__DATE__") + { + __result->append('"'); + __result->append(QDate::currentDate().toString().toUtf8()); + __result->append('"'); + continue; + } + + else if (fast_name == "__TIME__") + { + __result->append('"'); + __result->append(QTime::currentTime().toString().toUtf8()); + __result->append('"'); + continue; + } + + } + + __result->append(name_begin, name_end - name_begin); + continue; + } + + if (! macro->function_like) + { + Macro *m = 0; + + if (! macro->definition.isEmpty()) + { + macro->hidden = true; + + QByteArray __tmp; + __tmp.reserve (256); + + MacroExpander expand_macro (env); + expand_macro (macro->definition.constBegin (), macro->definition.constEnd (), &__tmp); + generated_lines += expand_macro.lines; + + if (! __tmp.isEmpty ()) + { + const char *__tmp_begin = __tmp.constBegin(); + const char *__tmp_end = __tmp.constEnd(); + const char *__begin_id = skip_whitespaces (__tmp_begin, __tmp_end); + const char *__end_id = skip_identifier (__begin_id, __tmp_end); + + if (__end_id == __tmp_end) + { + const QByteArray __id (__begin_id, __end_id - __begin_id); + m = env.resolve (__id); + } + + if (! m) + *__result += __tmp; + } + + macro->hidden = false; + } + + if (! m) + continue; + + macro = m; + } + + // function like macro + const char *arg_it = skip_whitespaces (__first, __last); + + if (arg_it == __last || *arg_it != '(') + { + __result->append(name_begin, name_end - name_begin); + lines += skip_whitespaces.lines; + __first = arg_it; + continue; + } + + QVector<QByteArray> actuals; + actuals.reserve (5); + ++arg_it; // skip '(' + + MacroExpander expand_actual (env, frame); + + const char *arg_end = skip_argument_variadics (actuals, macro, arg_it, __last); + if (arg_it != arg_end) + { + const QByteArray actual (arg_it, arg_end - arg_it); + QByteArray expanded; + expand_actual (actual.constBegin (), actual.constEnd (), &expanded); + actuals.push_back (expanded); + arg_it = arg_end; + } + + while (arg_it != __last && *arg_end == ',') + { + ++arg_it; // skip ',' + + arg_end = skip_argument_variadics (actuals, macro, arg_it, __last); + const QByteArray actual (arg_it, arg_end - arg_it); + QByteArray expanded; + expand_actual (actual.constBegin (), actual.constEnd (), &expanded); + actuals.push_back (expanded); + arg_it = arg_end; + } + + if (! (arg_it != __last && *arg_it == ')')) + return __last; + + ++arg_it; // skip ')' + __first = arg_it; + + pp_frame frame (macro, actuals); + MacroExpander expand_macro (env, &frame); + macro->hidden = true; + expand_macro (macro->definition.constBegin (), macro->definition.constEnd (), __result); + macro->hidden = false; + generated_lines += expand_macro.lines; + } + else + __result->append(*__first++); + } + + return __first; +} + +const char *MacroExpander::skip_argument_variadics (QVector<QByteArray> const &__actuals, + Macro *__macro, + const char *__first, const char *__last) +{ + const char *arg_end = skip_argument (__first, __last); + + while (__macro->variadics && __first != arg_end && arg_end != __last && *arg_end == ',' + && (__actuals.size () + 1) == __macro->formals.size ()) + { + arg_end = skip_argument (++arg_end, __last); + } + + return arg_end; +} diff --git a/src/plugins/cpptools/rpp/pp-macro-expander.h b/src/plugins/cpptools/rpp/pp-macro-expander.h new file mode 100644 index 0000000000..bdf21a421b --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-macro-expander.h @@ -0,0 +1,103 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_MACRO_EXPANDER_H +#define PP_MACRO_EXPANDER_H + +namespace rpp { + + struct pp_frame + { + Macro *expanding_macro; + const QVector<QByteArray> actuals; + + pp_frame (Macro *expanding_macro, const QVector<QByteArray> &actuals) + : expanding_macro (expanding_macro), + actuals (actuals) + { } + }; + + class MacroExpander + { + Environment &env; + pp_frame *frame; + + pp_skip_number skip_number; + pp_skip_identifier skip_identifier; + pp_skip_string_literal skip_string_literal; + pp_skip_char_literal skip_char_literal; + pp_skip_argument skip_argument; + pp_skip_comment_or_divop skip_comment_or_divop; + pp_skip_blanks skip_blanks; + pp_skip_whitespaces skip_whitespaces; + + const QByteArray *resolve_formal (const QByteArray &name); + + public: + MacroExpander (Environment &env, pp_frame *frame = 0); + + const char *operator () (const char *first, const char *last, + QByteArray *result); + + const char *skip_argument_variadics (const QVector<QByteArray> &actuals, + Macro *macro, + const char *first, const char *last); + + public: // attributes + int lines; + int generated_lines; + }; + +} // namespace rpp + +#endif // PP_MACRO_EXPANDER_H + diff --git a/src/plugins/cpptools/rpp/pp-macro.h b/src/plugins/cpptools/rpp/pp-macro.h new file mode 100644 index 0000000000..b220168989 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-macro.h @@ -0,0 +1,95 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_MACRO_H +#define PP_MACRO_H + +#include <QByteArray> +#include <QVector> + +namespace rpp { + + struct Macro + { + QByteArray name; + QByteArray definition; + QVector<QByteArray> formals; + QByteArray fileName; + int line; + int lines; + Macro *next; + unsigned hashcode; + + union + { + unsigned state; + + struct + { + unsigned hidden: 1; + unsigned function_like: 1; + unsigned variadics: 1; + }; + }; + + inline Macro(): + line(0), + lines(0), + next(0), + hashcode(0), + state(0) + { } + }; + +} // namespace rpp + +#endif // PP_MACRO_H diff --git a/src/plugins/cpptools/rpp/pp-scanner.h b/src/plugins/cpptools/rpp/pp-scanner.h new file mode 100644 index 0000000000..d9036d8855 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-scanner.h @@ -0,0 +1,380 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_SCANNER_H +#define PP_SCANNER_H + +namespace rpp { + +struct pp_skip_blanks +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (*__first == '\\') + { + const char *__begin = __first; + ++__begin; + + if (__begin != __last && *__begin == '\n') + ++__first; + else + break; + } + else if (*__first == '\n' || !pp_isspace (*__first)) + break; + } + + return __first; + } +}; + +struct pp_skip_whitespaces +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (! pp_isspace (*__first)) + break; + } + + return __first; + } +}; + +struct pp_skip_comment_or_divop +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + enum { + MAYBE_BEGIN, + BEGIN, + MAYBE_END, + END, + IN_COMMENT, + IN_CXX_COMMENT + } state (MAYBE_BEGIN); + + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + switch (state) + { + default: + assert (0); + break; + + case MAYBE_BEGIN: + if (*__first != '/') + return __first; + + state = BEGIN; + break; + + case BEGIN: + if (*__first == '*') + state = IN_COMMENT; + else if (*__first == '/') + state = IN_CXX_COMMENT; + else + return __first; + break; + + case IN_COMMENT: + if (*__first == '*') + state = MAYBE_END; + break; + + case IN_CXX_COMMENT: + if (*__first == '\n') + return __first; + break; + + case MAYBE_END: + if (*__first == '/') + state = END; + else if (*__first != '*') + state = IN_COMMENT; + break; + + case END: + return __first; + } + } + + return __first; + } +}; + +struct pp_skip_identifier +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (! pp_isalnum (*__first) && *__first != '_') + break; + } + + return __first; + } +}; + +struct pp_skip_number +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + if (! pp_isalnum (*__first) && *__first != '.') + break; + } + + return __first; + } +}; + +struct pp_skip_string_literal +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + enum { + BEGIN, + IN_STRING, + QUOTE, + END + } state (BEGIN); + + lines = 0; + + for (; __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + switch (state) + { + default: + assert (0); + break; + + case BEGIN: + if (*__first != '\"') + return __first; + state = IN_STRING; + break; + + case IN_STRING: + if (! (*__first != '\n')) + return __last; + + if (*__first == '\"') + state = END; + else if (*__first == '\\') + state = QUOTE; + break; + + case QUOTE: + state = IN_STRING; + break; + + case END: + return __first; + } + } + + return __first; + } +}; + +struct pp_skip_char_literal +{ + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + enum { + BEGIN, + IN_STRING, + QUOTE, + END + } state (BEGIN); + + lines = 0; + + for (; state != END && __first != __last; lines += (*__first != '\n' ? 0 : 1), ++__first) + { + switch (state) + { + default: + assert (0); + break; + + case BEGIN: + if (*__first != '\'') + return __first; + state = IN_STRING; + break; + + case IN_STRING: + if (! (*__first != '\n')) + return __last; + + if (*__first == '\'') + state = END; + else if (*__first == '\\') + state = QUOTE; + break; + + case QUOTE: + state = IN_STRING; + break; + } + } + + return __first; + } +}; + +struct pp_skip_argument +{ + pp_skip_identifier skip_number; + pp_skip_identifier skip_identifier; + pp_skip_string_literal skip_string_literal; + pp_skip_char_literal skip_char_literal; + pp_skip_comment_or_divop skip_comment_or_divop; + int lines; + + + const char *operator () (const char *__first, const char *__last) + { + int depth = 0; + lines = 0; + + while (__first != __last) + { + if (!depth && (*__first == ')' || *__first == ',')) + break; + else if (*__first == '(') + ++depth, ++__first; + else if (*__first == ')') + --depth, ++__first; + else if (*__first == '\"') + { + __first = skip_string_literal (__first, __last); + lines += skip_string_literal.lines; + } + else if (*__first == '\'') + { + __first = skip_char_literal (__first, __last); + lines += skip_char_literal.lines; + } + else if (*__first == '/') + { + __first = skip_comment_or_divop (__first, __last); + lines += skip_comment_or_divop.lines; + } + else if (pp_isalpha (*__first) || *__first == '_') + { + __first = skip_identifier (__first, __last); + lines += skip_identifier.lines; + } + else if (pp_isdigit (*__first)) + { + __first = skip_number (__first, __last); + lines += skip_number.lines; + } + else if (*__first == '\n') + { + ++__first; + ++lines; + } + else + ++__first; + } + + return __first; + } +}; + +} // namespace rpp + +#endif // PP_SCANNER_H + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/src/plugins/cpptools/rpp/pp-symbol.h b/src/plugins/cpptools/rpp/pp-symbol.h new file mode 100644 index 0000000000..37e2a623e5 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp-symbol.h @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + diff --git a/src/plugins/cpptools/rpp/pp.h b/src/plugins/cpptools/rpp/pp.h new file mode 100644 index 0000000000..52411f7624 --- /dev/null +++ b/src/plugins/cpptools/rpp/pp.h @@ -0,0 +1,76 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** 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 +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** 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 +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/* + Copyright 2005 Roberto Raggi <roberto@kdevelop.org> + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef PP_H +#define PP_H + +#if defined(_WIN64) || defined(WIN64) || defined(__WIN64__) \ + || defined(_WIN32) || defined(WIN32) || defined(__WIN32__) +# define PP_OS_WIN +#endif + +#include <cassert> +#include <cstring> +#include <cctype> + +#include "pp-fwd.h" +#include "pp-cctype.h" +#include "pp-symbol.h" +#include "pp-internal.h" +#include "pp-macro.h" +#include "pp-environment.h" +#include "pp-scanner.h" +#include "pp-macro-expander.h" +#include "pp-engine.h" +#include "pp-client.h" + +#endif // PP_H diff --git a/src/plugins/cpptools/rpp/rpp.pri b/src/plugins/cpptools/rpp/rpp.pri new file mode 100644 index 0000000000..f47976e6fe --- /dev/null +++ b/src/plugins/cpptools/rpp/rpp.pri @@ -0,0 +1,20 @@ +DEPENDPATH += $$PWD +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/pp-cctype.h \ + $$PWD/pp-engine.h \ + $$PWD/pp-environment.h \ + $$PWD/pp-fwd.h \ + $$PWD/pp-internal.h \ + $$PWD/pp-macro-expander.h \ + $$PWD/pp-macro.h \ + $$PWD/pp-scanner.h \ + $$PWD/pp-symbol.h \ + $$PWD/pp.h \ + $$PWD/pp-client.h + +SOURCES += $$PWD/pp-engine.cpp \ + $$PWD/pp-environment.cpp \ + $$PWD/pp-macro-expander.cpp + + |