From 7ee7055485467457614a5f07aea1dcd4c47d14cd Mon Sep 17 00:00:00 2001 From: Roberto Raggi <roberto.raggi@nokia.com> Date: Tue, 2 Jun 2009 14:56:03 +0200 Subject: [PATCH] Initial support for semantic searches. --- src/plugins/cpptools/cppmodelmanager.cpp | 1 + src/plugins/cpptools/cpptoolsplugin.cpp | 242 ++++++++++++++++++++++- src/plugins/cpptools/cpptoolsplugin.h | 41 ++++ src/shared/cplusplus/TranslationUnit.cpp | 6 + src/shared/cplusplus/TranslationUnit.h | 2 + 5 files changed, 290 insertions(+), 2 deletions(-) diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 0967cfdd092..657286bec53 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -368,6 +368,7 @@ QByteArray CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type) path += frameworkName; path += QLatin1String(".framework/Headers/"); path += name; + path = QDir::cleanPath(path); QByteArray contents; if (includeFile(path, &contents)) { fileName = path; diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index 5ea430bf42a..0f9ab461d53 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -28,7 +28,6 @@ **************************************************************************/ #include "cpptoolsplugin.h" - #include "completionsettingspage.h" #include "cppfilesettingspage.h" #include "cppclassesfilter.h" @@ -38,14 +37,34 @@ #include "cpptoolsconstants.h" #include "cppquickopenfilter.h" +#include <extensionsystem/pluginmanager.h> + #include <coreplugin/icore.h> #include <coreplugin/mimedatabase.h> #include <coreplugin/coreconstants.h> #include <coreplugin/uniqueidmanager.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/progressmanager/progressmanager.h> #include <cppeditor/cppeditorconstants.h> +#include <QtCore/QtConcurrentRun> +#include <QtCore/QFutureSynchronizer> +#include <qtconcurrent/runextensions.h> + +#include <find/ifindfilter.h> +#include <find/searchresultwindow.h> +#include <utils/filesearch.h> + +#include <Control.h> +#include <AST.h> +#include <ASTVisitor.h> +#include <TranslationUnit.h> +#include <PrettyPrinter.h> + +#include <cplusplus/PreprocessorEnvironment.h> +#include <cplusplus/pp.h> + #include <QtCore/QtPlugin> #include <QtCore/QFileInfo> #include <QtCore/QDir> @@ -54,13 +73,230 @@ #include <QtGui/QMenu> #include <QtGui/QAction> +#include <sstream> + using namespace CppTools::Internal; +using namespace CPlusPlus; enum { debug = 0 }; - CppToolsPlugin *CppToolsPlugin::m_instance = 0; +namespace { + +class SimpleClient: public Client +{ + Environment _env; + QPointer<CppModelManager> _modelManager; + Snapshot _snapshot; + Preprocessor _preproc; + QSet<QString> _merged; + +public: + SimpleClient(QPointer<CppModelManager> modelManager) + : _modelManager(modelManager), + _snapshot(_modelManager->snapshot()), + _preproc(this, &_env) + { } + + QByteArray run(QString fileName, const QByteArray &source) + { + const QByteArray preprocessed = _preproc(fileName, source); + return preprocessed; + } + + virtual void sourceNeeded(QString &fileName, IncludeType, unsigned) + { mergeEnvironment(fileName); } + + virtual void macroAdded(const Macro &) {} + + virtual void startExpandingMacro(unsigned, + const Macro &, + const QByteArray &, + const QVector<MacroArgumentReference> &) {} + + virtual void stopExpandingMacro(unsigned, const Macro &) {} + + virtual void startSkippingBlocks(unsigned) {} + virtual void stopSkippingBlocks(unsigned) {} + + void mergeEnvironment(const QString &fileName) + { + if (! _merged.contains(fileName)) { + _merged.insert(fileName); + + if (Document::Ptr doc = _snapshot.value(fileName)) { + foreach (const Document::Include &i, doc->includes()) + mergeEnvironment(i.fileName()); + + _env.addMacros(doc->definedMacros()); + } + } + } +}; + +class FindClass: protected ASTVisitor +{ + QFutureInterface<Core::Utils::FileSearchResult> &_future; + Document::Ptr _doc; + Snapshot _snapshot; + + QByteArray _source; + QString _text; + QTextDocument::FindFlags _findFlags; + +public: + FindClass(QFutureInterface<Core::Utils::FileSearchResult> &future, Document::Ptr doc, Snapshot snapshot) + : ASTVisitor(doc->control()), + _future(future), + _doc(doc), + _snapshot(snapshot) + { + } + + void operator()(AST *ast, const QByteArray &source, const QString &text, + QTextDocument::FindFlags findFlags) + { + _source = source; + _text = text; + _findFlags = findFlags; + accept(ast); + } + +protected: + using ASTVisitor::visit; + + virtual bool visit(ClassSpecifierAST *ast) + { + if (ast->name) { + Qt::CaseSensitivity cs = Qt::CaseInsensitive; + if (_findFlags & QTextDocument::FindCaseSensitively) + cs = Qt::CaseSensitive; + + Token start = tokenAt(ast->name->firstToken()); + Token end = tokenAt(ast->name->lastToken() - 1); + const QString className = QString::fromUtf8(_source.constData() + start.begin(), + end.end() - start.begin()); + + int idx = className.indexOf(_text, cs); + if (idx != -1) { + const char *beg = _source.constData(); + const char *cp = beg + start.offset; + for (; cp != beg - 1; --cp) { + if (*cp == '\n') + break; + } + + ++cp; + + const char *lineEnd = cp + 1; + for (; *lineEnd; ++lineEnd) { + if (*lineEnd == '\n') + break; + } + + const QString matchingLine = QString::fromUtf8(cp, lineEnd - cp); + + unsigned line, col; + getTokenStartPosition(ast->name->firstToken(), &line, &col); + + _future.reportResult(Core::Utils::FileSearchResult(QDir::toNativeSeparators(_doc->fileName()), + line, matchingLine, + col + idx - 1, _text.length())); + } + } + + return true; + } +}; + +static void searchClassDeclarations(QFutureInterface<Core::Utils::FileSearchResult> &future, + QPointer<CppModelManager> modelManager, + QString text, + QTextDocument::FindFlags findFlags) +{ + const Snapshot snapshot = modelManager->snapshot(); + + future.setProgressRange(0, snapshot.size()); + future.setProgressValue(0); + + int progress = 0; + foreach (Document::Ptr doc, snapshot) { + const QString fileName = doc->fileName(); + + QFile file(fileName); + if (! file.open(QFile::ReadOnly)) + continue; + + const QString contents = QTextStream(&file).readAll(); + + SimpleClient r(modelManager); + const QByteArray source = r.run(fileName, contents.toUtf8()); + + Document::Ptr newDoc = Document::create(fileName); + newDoc->setSource(source); + newDoc->parse(); + + FindClass findClass(future, newDoc, snapshot); + findClass(newDoc->translationUnit()->ast(), source, text, findFlags); + + future.setProgressValue(++progress); + } +} + +} // end of anonymous namespace + +FindClassDeclarations::FindClassDeclarations(CppModelManager *modelManager) + : _modelManager(modelManager), + _resultWindow(ExtensionSystem::PluginManager::instance()->getObject<Find::SearchResultWindow>()) +{ + m_watcher.setPendingResultsLimit(1); + connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(displayResult(int))); + connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished())); +} + +void FindClassDeclarations::findAll(const QString &txt, QTextDocument::FindFlags findFlags) +{ + _resultWindow->clearContents(); + _resultWindow->popup(true); + + Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager(); + + QFuture<Core::Utils::FileSearchResult> result = + QtConcurrent::run(&searchClassDeclarations, _modelManager, txt, findFlags); + + m_watcher.setFuture(result); + + Core::FutureProgress *progress = progressManager->addTask(result, tr("Search class"), + CppTools::Constants::TASK_INDEX, + Core::ProgressManager::CloseOnSuccess); + + connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup())); +} + +void FindClassDeclarations::displayResult(int index) +{ + Core::Utils::FileSearchResult result = m_watcher.future().resultAt(index); + Find::ResultWindowItem *item = _resultWindow->addResult(result.fileName, + result.lineNumber, + result.matchingLine, + result.matchStart, + result.matchLength); + if (item) + connect(item, SIGNAL(activated(const QString&,int,int)), + this, SLOT(openEditor(const QString&,int,int))); +} + +void FindClassDeclarations::searchFinished() +{ + emit changed(); +} + +void FindClassDeclarations::openEditor(const QString &fileName, int line, int column) +{ + TextEditor::BaseTextEditor::openEditorAt(fileName, line, column); +} + CppToolsPlugin::CppToolsPlugin() : m_context(-1), m_modelManager(0), @@ -95,6 +331,8 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) addAutoReleasedObject(new CompletionSettingsPage(m_completion)); addAutoReleasedObject(new CppFileSettingsPage(m_fileSettings)); + addAutoReleasedObject(new FindClassDeclarations(m_modelManager)); + // Menus Core::ActionContainer *mtools = am->actionContainer(Core::Constants::M_TOOLS); Core::ActionContainer *mcpptools = am->createMenu(CppTools::Constants::M_TOOLS_CPP); diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h index dacc946fe10..eb3380f1fa9 100644 --- a/src/plugins/cpptools/cpptoolsplugin.h +++ b/src/plugins/cpptools/cpptoolsplugin.h @@ -32,13 +32,28 @@ #include <extensionsystem/iplugin.h> #include <projectexplorer/projectexplorer.h> +#include <find/ifindfilter.h> +#include <utils/filesearch.h> + +#include <QtGui/QTextDocument> #include <QtCore/QSharedPointer> +#include <QtCore/QFutureInterface> +#include <QtCore/QPointer> +#include <QtCore/QFutureWatcher> QT_BEGIN_NAMESPACE class QFileInfo; class QDir; QT_END_NAMESPACE +namespace CPlusPlus { +class Snapshot; +} + +namespace Find { +class SearchResultWindow; +} + namespace CppTools { namespace Internal { @@ -46,6 +61,32 @@ class CppCodeCompletion; class CppModelManager; struct CppFileSettings; +class FindClassDeclarations: public Find::IFindFilter +{ + Q_OBJECT + +public: + FindClassDeclarations(CppModelManager *modelManager); + + // Find::IFindFilter + virtual QString id() const { return QLatin1String("CppTools.Find.ClassDeclarations"); } + virtual QString name() const { return tr("Class Declarations"); } + virtual bool isEnabled() const { return true; } + virtual QKeySequence defaultShortcut() const { return QKeySequence(); } + virtual void findAll(const QString &txt, QTextDocument::FindFlags findFlags); + +protected Q_SLOTS: + void displayResult(int); + void searchFinished(); + void openEditor(const QString&, int, int); + +private: + QPointer<CppModelManager> _modelManager; + Find::SearchResultWindow *_resultWindow; + QFutureWatcher<Core::Utils::FileSearchResult> m_watcher; +}; + + class CppToolsPlugin : public ExtensionSystem::IPlugin { Q_DISABLE_COPY(CppToolsPlugin) diff --git a/src/shared/cplusplus/TranslationUnit.cpp b/src/shared/cplusplus/TranslationUnit.cpp index d6afcb3e2f9..2f2024dc2d4 100644 --- a/src/shared/cplusplus/TranslationUnit.cpp +++ b/src/shared/cplusplus/TranslationUnit.cpp @@ -465,6 +465,12 @@ void TranslationUnit::fatal(unsigned index, const char *format, ...) exit(EXIT_FAILURE); } +unsigned TranslationUnit::findPreviousLineOffset(unsigned tokenIndex) const +{ + unsigned lineOffset = _lineOffsets[findLineNumber(_tokens->at(tokenIndex).offset)]; + return lineOffset; +} + void TranslationUnit::showErrorLine(unsigned index, unsigned column, FILE *out) { unsigned lineOffset = _lineOffsets[findLineNumber(_tokens->at(index).offset)]; diff --git a/src/shared/cplusplus/TranslationUnit.h b/src/shared/cplusplus/TranslationUnit.h index b4830e525dd..609e9b7309a 100644 --- a/src/shared/cplusplus/TranslationUnit.h +++ b/src/shared/cplusplus/TranslationUnit.h @@ -142,6 +142,8 @@ public: unsigned line, StringLiteral *fileName); + unsigned findPreviousLineOffset(unsigned tokenIndex) const; + public: struct PPLine { unsigned offset; -- GitLab