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