[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdev-clang/wip/maciej/tooling] refactoring: Introduced worker thread for time-consuming Clang actio
From:       Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5 () gmail ! com>
Date:       2015-08-01 0:57:07
Message-ID: E1ZLL6p-00036k-HX () scm ! kde ! org
[Download RAW message or body]

Git commit 91c0926d27511067330f7b82696c822a0244a6f4 by Maciej Poleski.
Committed on 01/08/2015 at 00:56.
Pushed by mpoleski into branch 'wip/maciej/tooling'.

Introduced worker thread for time-consuming Clang actions. Move populating of context \
menu to background.

M  +3    -0    refactoring/CMakeLists.txt
A  +117  -0    refactoring/contextmenumutator.cpp     [License: LGPL (v2+)]
C  +26   -24   refactoring/contextmenumutator.h [from: \
refactoring/refactoringcontext.h - 054% similarity] M  +0    -7    \
refactoring/interface.cpp M  +0    -8    refactoring/interface.h
M  +6    -90   refactoring/kdevrefactorings.cpp
M  +8    -9    refactoring/kdevrefactorings.h
M  +78   -4    refactoring/refactoringcontext.cpp
M  +97   -2    refactoring/refactoringcontext.h
A  +68   -0    refactoring/refactoringcontext_worker.cpp     [License: LGPL (v2+)]
A  +63   -0    refactoring/refactoringcontext_worker.h     [License: LGPL (v2+)]
M  +31   -11   refactoring/refactoringmanager.cpp
M  +19   -17   refactoring/refactoringmanager.h

http://commits.kde.org/kdev-clang/91c0926d27511067330f7b82696c822a0244a6f4

diff --git a/refactoring/CMakeLists.txt b/refactoring/CMakeLists.txt
index 6c96491..c9b7e90 100644
--- a/refactoring/CMakeLists.txt
+++ b/refactoring/CMakeLists.txt
@@ -26,6 +26,9 @@ set(SRCS
     encapsulatefieldrefactoring.cpp
     encapsulatefielddialog.cpp
     encapsulatefieldrefactoring_changepack.cpp
+    refactoringcontext_worker.cpp
+    contextmenumutator.cpp
+    actionwatcher.cpp
 )
 
 add_library(kdevclangrefactor STATIC
diff --git a/refactoring/contextmenumutator.cpp b/refactoring/contextmenumutator.cpp
new file mode 100644
index 0000000..5264a42
--- /dev/null
+++ b/refactoring/contextmenumutator.cpp
@@ -0,0 +1,117 @@
+/*
+    This file is part of KDevelop
+
+    Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+// Qt
+#include <QAction>
+#include <QMenu>
+
+// KF5
+#include <KF5/KI18n/klocalizedstring.h>
+
+// KDevelop
+#include <interfaces/contextmenuextension.h>
+#include <language/interfaces/editorcontext.h>
+
+#include "contextmenumutator.h"
+#include "refactoringmanager.h"
+#include "kdevrefactorings.h"
+#include "actionwatcher.h"
+
+using namespace KDevelop;
+
+ContextMenuMutator::ContextMenuMutator(ContextMenuExtension &extension, \
RefactoringManager *parent) +    : QObject(parent)
+    , m_placeholder(new QAction(i18n("preparing list..."), this))
+{
+    extension.addAction(ContextMenuExtension::RefactorGroup, m_placeholder);
+}
+
+RefactoringManager *ContextMenuMutator::parent()
+{
+    return static_cast<RefactoringManager *>(QObject::parent());
+}
+
+// Create submenu for refactoring actions if necessary and possible
+QWidget *ContextMenuMutator::menuForWidget(QWidget *widget)
+{
+    QMenu * const menu = dynamic_cast<QMenu *>(widget);
+    if (menu) {
+        int sectionSize = 0;
+        bool found = false;
+        for (QAction *a : menu->actions()) {
+            if (!a->isSeparator()) {
+                sectionSize++;
+                if (a == m_placeholder) {
+                    found = true;
+                }
+            } else {
+                if (!found) {
+                    sectionSize = 0;
+                } else {
+                    break;
+                }
+            }
+        }
+        Q_ASSERT(sectionSize > 0);
+        if (sectionSize > 1) {
+            // do nothing
+            return widget;
+        } else {
+            // make submenu
+            QMenu *submenu = new QMenu(i18n("Refactor"), menu);
+            menu->insertMenu(m_placeholder, submenu);
+            return submenu;
+        }
+    } else {
+        // We can't create submenu
+        return widget;
+    }
+}
+
+void ContextMenuMutator::endFillingContextMenu(const QVector<Refactoring *> \
&refactorings) +{
+    QList<QAction *> actions;
+    auto refactoringContext = parent()->parent()->refactoringContext();
+    for (auto refactorAction : refactorings) {
+        QAction *action = new QAction(refactorAction->name(), parent());
+        refactorAction->setParent(action);  // delete as necessary
+        connect(action, &QAction::triggered, [this, refactoringContext, \
refactorAction]() +        {
+            // TODO: don't use refactorThis
+            auto changes = Refactorings::refactorThis(refactoringContext, \
refactorAction, +                                                      {}, {});
+            // FIXME:
+            // use background thread
+            // ... provided with ability to show GUI
+            // show busy indicator
+
+            changes.applyAllChanges();
+        });
+        actions.push_back(action);
+    }
+    for (QWidget *w : m_placeholder->associatedWidgets()) {
+        menuForWidget(w)->insertActions(m_placeholder, actions);
+    }
+    for (QAction *a : actions) {
+        new ActionWatcher(a);
+    }
+    deleteLater();
+}
diff --git a/refactoring/refactoringcontext.h b/refactoring/contextmenumutator.h
similarity index 54%
copy from refactoring/refactoringcontext.h
copy to refactoring/contextmenumutator.h
index 91001cb..932e8c3 100644
--- a/refactoring/refactoringcontext.h
+++ b/refactoring/contextmenumutator.h
@@ -19,43 +19,45 @@
     Boston, MA 02110-1301, USA.
 */
 
-#ifndef KDEV_CLANG_REFACTORINGCONTEXT_H
-#define KDEV_CLANG_REFACTORINGCONTEXT_H
+#ifndef KDEV_CLANG_CONTEXTMENUMUTATOR_H
+#define KDEV_CLANG_CONTEXTMENUMUTATOR_H
 
-// base class
+// Qt
 #include <QObject>
 
-// KF5
-#include <KTextEditor/ktexteditor/cursor.h>
-
-// LLVM
-#include <llvm/Support/ErrorOr.h>
-
-// Clang
-#include <clang/Tooling/CompilationDatabase.h>
+class QAction;
+class QWidget;
 
 namespace KDevelop
 {
-class IDocumentController;
-};
+class ContextMenuExtension;
+
+class EditorContext;
+}
 
-class DocumentCache;
+class RefactoringManager;
 
-// TODO: join with DocumentCache, handle CompilationDatabase here
-class RefactoringContext : public QObject
+class Refactoring;
+
+class ContextMenuMutator : public QObject
 {
     Q_OBJECT;
-    Q_DISABLE_COPY(RefactoringContext);
-
+    Q_DISABLE_COPY(ContextMenuMutator);
 public:
-    RefactoringContext(std::unique_ptr<clang::tooling::CompilationDatabase> \
database); +    ContextMenuMutator(KDevelop::ContextMenuExtension &extension, \
RefactoringManager *parent); +
+    RefactoringManager *parent();
+
+private:
+    QWidget* menuForWidget(QWidget *widget);
 
-    llvm::ErrorOr<unsigned> offset(const std::string &sourceFile,
-                                   const KTextEditor::Cursor &position) const;
+public slots:
+    void endFillingContextMenu(const QVector<Refactoring *> &refactorings);
 
-    std::unique_ptr<clang::tooling::CompilationDatabase> database;
-    DocumentCache *cache;
+private:
+    QAction *m_placeholder;
 };
 
+Q_DECLARE_METATYPE(ContextMenuMutator*);
 
-#endif //KDEV_CLANG_REFACTORINGCONTEXT_H
+#endif //KDEV_CLANG_CONTEXTMENUMUTATOR_H
diff --git a/refactoring/interface.cpp b/refactoring/interface.cpp
index 96e1260..63379bc 100644
--- a/refactoring/interface.cpp
+++ b/refactoring/interface.cpp
@@ -80,13 +80,6 @@ void releaseCompilationDatabase(CompilationDatabase db)
 
 ///////////////// Refactoring Context
 
-RefactoringsContext createRefactoringsContext(CompilationDatabase db)
-{
-    auto result = new RefactoringContext(std::move(db->database));
-    releaseCompilationDatabase(db);
-    return result;
-}
-
 KDevelop::DocumentChangeSet refactorThis(RefactoringsContext rc, RefactoringKind \
refactoringKind,  const QUrl &sourceFile,
                                          const KTextEditor::Cursor &position)
diff --git a/refactoring/interface.h b/refactoring/interface.h
index 4c4fb3b..5ba386b 100644
--- a/refactoring/interface.h
+++ b/refactoring/interface.h
@@ -123,14 +123,6 @@ void releaseCompilationDatabase(CompilationDatabase db);
 std::vector<std::string> sources(CompilationDatabase db);
 
 /**
- * Prepare and return main context for refactorings in KDevelop.
- *
- * @param db Up-to-date compilation database
- * @return Context for future use with refactorings
- */
-RefactoringsContext createRefactoringsContext(CompilationDatabase db);
-
-/**
  * Invokes selected refactoring action on selected place in file.
  *
  * @param rc Initialized refactorings context
diff --git a/refactoring/kdevrefactorings.cpp b/refactoring/kdevrefactorings.cpp
index 1e9169b..07ad6b6 100644
--- a/refactoring/kdevrefactorings.cpp
+++ b/refactoring/kdevrefactorings.cpp
@@ -19,27 +19,12 @@
     Boston, MA 02110-1301, USA.
 */
 
-#include "kdevrefactorings.h"
-
-// Qt
-#include <QAction>
-#include <QTimer>
-
-// KF5
-#include <KJob>
-
 // KDevelop
 #include <language/interfaces/editorcontext.h>
-#include <interfaces/icore.h>
-#include <interfaces/iproject.h>
-#include <interfaces/iprojectcontroller.h>
-#include <project/interfaces/ibuildsystemmanager.h>
-#include <project/interfaces/iprojectbuilder.h>
-#include <project/projectmodel.h>
 
-#include "interface.h"
+#include "kdevrefactorings.h"
+#include "refactoringcontext.h"
 #include "refactoringmanager.h"
-#include "debug.h"
 
 #include "../clangsupport.h"
 
@@ -48,87 +33,18 @@ using namespace KDevelop;
 using namespace Refactorings;
 
 KDevRefactorings::KDevRefactorings(ClangSupport *parent)
-        : QObject(parent)
+    : QObject(parent)
+    , m_refactoringsContext(new RefactoringContext(this))
+    , m_refactoringManager(new RefactoringManager(this))
 {
-    m_refactoringManager = new RefactoringManager(this);
-    connect(parent->core()->projectController(), &IProjectController::projectOpened,
-            this, &KDevRefactorings::projectOpened);
-    // handle projects() - alread opened projects
-
 }
 
 void KDevRefactorings::fillContextMenu(ContextMenuExtension &extension, Context \
*context)  {
     if (EditorContext *ctx = dynamic_cast<EditorContext *>(context)) {
-        // NOTE: I assume ctx->positions() is "here" position
-        auto refactorings = \
                m_refactoringManager->allApplicableRefactorings(m_refactoringsContext,
                
-                                                                            \
                ctx->url(),
-                                                                            \
                ctx->position());
-        for (auto refactorAction : refactorings) {
-            QAction *action = new QAction(refactorAction->name(), this);
-            refactorAction->setParent(action);  // delete as necessary
-            connect(action, &QAction::triggered, [this, refactorAction, ctx]()
-            {
-                // TODO: don't use refactorThis
-                auto changes = refactorThis(m_refactoringsContext, refactorAction,
-                                            ctx->url(),
-                                            ctx->position());
-                // FIXME:
-                // use background thread
-                // ... provided with ability to show GUI
-                // show busy indicator
-
-                changes.applyAllChanges();
-                // NOTE: cache of ClangTool and FileSystem must be updated after \
                application to
-                // reflect changes in files
-            });
-
-            extension.addAction(ContextMenuExtension::RefactorGroup, action);
-        }
+        m_refactoringManager->fillContextMenu(extension, ctx);
     } else {
         // I assume the above works anytime we ask for context menu for code
         Q_ASSERT(!context->hasType(Context::CodeContext));
     }
 }
-
-void KDevRefactorings::projectOpened(IProject *project)
-{
-    Q_ASSERT(project);
-    refactorDebug() << project->name() << "opened";
-    auto projectBuilder = project->buildSystemManager()->builder();
-    // IProjectBuilder declares signal configured(), but is unused...
-
-    // FIXME: configure only when necessary (and always when necessary)
-    auto configureJob = projectBuilder->configure(project);
-    connect(configureJob, &KJob::result, this, [this, project]()
-    {
-        projectConfigured(project);
-    });
-    configureJob->start();
-    // wait for above connection to trigger further actions
-}
-
-void KDevRefactorings::projectConfigured(IProject *project)
-{
-    Q_ASSERT(project);
-    refactorDebug() << project->name() << "configured";
-    auto buildSystemManager = project->buildSystemManager();
-    Path buildPath = buildSystemManager->buildDirectory(project->projectItem());
-    refactorDebug() << "build path:" << buildPath;
-
-    // FIXME: do it async (in background)
-    // FIXME: handle non-CMake project
-    QString errorMessage;
-    auto compilationDatabase = Refactorings::createCompilationDatabase(
-            buildPath.toLocalFile().toStdString(), Refactorings::ProjectKind::CMAKE, \
                errorMessage);
-    if (!compilationDatabase) {
-        // TODO: show message for that
-        refactorDebug() << "Cannot create compilation database for" << \
                project->name() << ":" <<
-                        errorMessage;
-        return;
-    }
-    auto refactoringsContext = \
                Refactorings::createRefactoringsContext(compilationDatabase);
-    Q_ASSERT(refactoringsContext);
-    m_refactoringsContext = refactoringsContext;
-    refactorDebug() << "RefactoringsContext sucessfully (re)generated!";
-}
diff --git a/refactoring/kdevrefactorings.h b/refactoring/kdevrefactorings.h
index 13d7b2b..f44d68d 100644
--- a/refactoring/kdevrefactorings.h
+++ b/refactoring/kdevrefactorings.h
@@ -35,8 +35,10 @@
 #include <interfaces/iproject.h>
 
 #include "interface.h"
+#include "refactoring.h"
 
 class ClangSupport;
+
 class RefactoringManager;
 
 /**
@@ -55,17 +57,14 @@ public:
     // TODO: Handle configuration of projects to regenerate CompilationDatabase
     // TODO: Handle above + changes in files (also creation) to update/regenerate \
RefactoringsContext  
-private: // (slots)
-    // Only one project for now
-    void projectOpened(KDevelop::IProject* project);
-    void projectConfigured(KDevelop::IProject* project);
-
-    // TODO: (async)
-    void createRefactoringsContext();
+    RefactoringContext *refactoringContext()
+    {
+        return m_refactoringsContext;
+    }
 
 private:
-    Refactorings::RefactoringsContext m_refactoringsContext = nullptr;
-    RefactoringManager *m_refactoringManager;
+    RefactoringContext * const m_refactoringsContext;
+    RefactoringManager * const m_refactoringManager;
 };
 
 #endif //BUILD_REFACTORINGS
diff --git a/refactoring/refactoringcontext.cpp b/refactoring/refactoringcontext.cpp
index f9f90a7..6ee38ac 100644
--- a/refactoring/refactoringcontext.cpp
+++ b/refactoring/refactoringcontext.cpp
@@ -19,16 +19,46 @@
     Boston, MA 02110-1301, USA.
 */
 
-#include "refactoringcontext.h"
+// KF5
+#include <KJob>
+
+// KDevelop
+#include <interfaces/icore.h>
+#include <interfaces/iproject.h>
+#include <interfaces/iprojectcontroller.h>
+#include <project/interfaces/ibuildsystemmanager.h>
+#include <project/interfaces/iprojectbuilder.h>
+#include <project/projectmodel.h>
 
+#include "refactoringcontext.h"
+#include "kdevrefactorings.h"
 #include "documentcache.h"
+#include "refactoringcontext_worker.h"
 #include "utils.h"
+#include "debug.h"
 
-RefactoringContext::RefactoringContext(
-    std::unique_ptr<clang::tooling::CompilationDatabase> database)
-    : database(std::move(database))
+using namespace KDevelop;
+
+RefactoringContext::RefactoringContext(KDevRefactorings *parent)
+    : QObject(parent)
 {
+    qRegisterMetaType<std::function<void()>>();
     cache = new DocumentCache(this);
+    m_worker = new Worker(this);
+
+    connect(m_worker, &Worker::taskFinished, this, \
&RefactoringContext::invokeCallback); +    // Will not call-back if this \
RefactoringContext have been destroyed concurrently +
+    connect(ICore::self()->projectController(), &IProjectController::projectOpened,
+            this, &RefactoringContext::projectOpened);
+    // handle projects() - alread opened projects
+
+    m_worker->start();
+}
+
+KDevRefactorings *RefactoringContext::parent()
+{
+    return static_cast<KDevRefactorings *>(QObject::parent());
 }
 
 llvm::ErrorOr<unsigned> RefactoringContext::offset(const std::string &sourceFile,
@@ -36,3 +66,47 @@ llvm::ErrorOr<unsigned> RefactoringContext::offset(const \
std::string &sourceFile  {
     return toOffset(sourceFile, position, cache->refactoringTool(), cache);
 }
+
+void RefactoringContext::projectOpened(IProject *project)
+{
+    Q_ASSERT(project);
+    refactorDebug() << project->name() << "opened";
+    auto projectBuilder = project->buildSystemManager()->builder();
+    // IProjectBuilder declares signal configured(), but is unused...
+
+    // FIXME: configure only when necessary (and always when necessary)
+    auto configureJob = projectBuilder->configure(project);
+    connect(configureJob, &KJob::result, this, [this, project]()
+    {
+        projectConfigured(project);
+    });
+    configureJob->start();
+    // wait for above connection to trigger further actions
+}
+
+void RefactoringContext::projectConfigured(IProject *project)
+{
+    Q_ASSERT(project);
+    refactorDebug() << project->name() << "configured";
+    auto buildSystemManager = project->buildSystemManager();
+    Path buildPath = buildSystemManager->buildDirectory(project->projectItem());
+    refactorDebug() << "build path:" << buildPath;
+
+    // FIXME: do it async (in background)
+    // FIXME: handle non-CMake project
+    QString errorMessage;
+    database = makeCompilationDatabaseFromCMake(buildPath.toLocalFile().toStdString(),
 +                                                errorMessage);
+    if (!database) {
+        // TODO: show message for that
+        refactorDebug() << "Cannot create compilation database for" << \
project->name() << ":" << +                        errorMessage;
+        return;
+    }
+    refactorDebug() << "RefactoringsContext sucessfully (re)generated!";
+}
+
+void RefactoringContext::invokeCallback(std::function<void()> callback)
+{
+    callback();
+}
diff --git a/refactoring/refactoringcontext.h b/refactoring/refactoringcontext.h
index 91001cb..ce370b1 100644
--- a/refactoring/refactoringcontext.h
+++ b/refactoring/refactoringcontext.h
@@ -22,8 +22,9 @@
 #ifndef KDEV_CLANG_REFACTORINGCONTEXT_H
 #define KDEV_CLANG_REFACTORINGCONTEXT_H
 
-// base class
+// Qt
 #include <QObject>
+#include <QTimer>
 
 // KF5
 #include <KTextEditor/ktexteditor/cursor.h>
@@ -33,12 +34,17 @@
 
 // Clang
 #include <clang/Tooling/CompilationDatabase.h>
+#include <clang/Tooling/Refactoring.h>
 
 namespace KDevelop
 {
 class IDocumentController;
+
+class IProject;
 };
 
+class KDevRefactorings;
+
 class DocumentCache;
 
 // TODO: join with DocumentCache, handle CompilationDatabase here
@@ -47,15 +53,104 @@ class RefactoringContext : public QObject
     Q_OBJECT;
     Q_DISABLE_COPY(RefactoringContext);
 
+    class Worker;
+
 public:
-    RefactoringContext(std::unique_ptr<clang::tooling::CompilationDatabase> \
database); +    RefactoringContext(KDevRefactorings *parent);
+
+    KDevRefactorings *parent();
 
     llvm::ErrorOr<unsigned> offset(const std::string &sourceFile,
                                    const KTextEditor::Cursor &position) const;
 
+    template<typename Task, typename Callback>
+    void schedule(Task task, Callback callback);
+    template<typename Task, typename Callback>
+    void scheduleOnSingleFile(Task task, const std::string &filename, Callback \
callback); +
+private: // (slots)
+    // Only one project for now
+    void projectOpened(KDevelop::IProject *project);
+    void projectConfigured(KDevelop::IProject *project);
+
+private slots:
+    // Directly call callback on this thread
+    void invokeCallback(std::function<void()> callback);
+
+private:
+    template<typename Task, typename Callback>
+    static std::function<void(clang::tooling::RefactoringTool &,
+                              std::function<void(std::function<void()>)>)> \
composeTask( +        Task task, Callback callback);
+
+public:
     std::unique_ptr<clang::tooling::CompilationDatabase> database;
     DocumentCache *cache;
+
+private:
+    Worker *m_worker;
 };
 
+Q_DECLARE_METATYPE(std::function<void()>);
+
+template<typename Task, typename Callback>
+std::function<void(clang::tooling::RefactoringTool &,
+                   std::function<void(std::function<void()>)>)> \
RefactoringContext::composeTask( +    Task task, Callback callback)
+{
+    return [task, callback](
+        clang::tooling::RefactoringTool &tool,
+        std::function<void(std::function<void()>)> callbackScheduler)
+    {
+        auto result = task(tool);
+        // By value! (TODO C++14: move result to lambda instead of copying)
+        auto callbackInvoker = [callback, result]
+        {
+            callback(result);
+        };
+        callbackScheduler(callbackInvoker);
+    };
+};
+
+#include "refactoringcontext_worker.h"
+
+template<typename Task, typename Callback>
+void RefactoringContext::schedule(Task task, Callback callback)
+{
+    auto composedTask = composeTask(task, callback);
+    auto worker = m_worker;
+#if(QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
+    QTimer::singleShot(0, m_worker, [worker, composedTask]
+    {
+        worker->invoke(composedTask);
+    });
+#else
+    QMetaObject::invokeMethod(
+        m_worker, "invoke",
+        Q_ARG(std::function<void(clang::tooling::RefactoringTool & ,
+                  std::function<void(std::function<void()>)>)>, composedTask));
+#endif
+};
+
+template<typename Task, typename Callback>
+void RefactoringContext::scheduleOnSingleFile(Task task, const std::string \
&filename, +                                              Callback callback)
+{
+    auto composedTask = composeTask(task, callback);
+    auto worker = m_worker;
+#if(QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
+    QTimer::singleShot(0, m_worker, [worker, composedTask, filename]
+    {
+        worker->invokeOnSingleFile(composedTask, filename);
+    });
+#else
+    QMetaObject::invokeMethod(
+        m_worker, "invokeOnSingleFile",
+        Q_ARG(std::function<void(clang::tooling::RefactoringTool & ,
+                  std::function<void(std::function<void()>)>)>, composedTask),
+        Q_ARG(std::string, filename));
+#endif
+}
+
 
 #endif //KDEV_CLANG_REFACTORINGCONTEXT_H
diff --git a/refactoring/refactoringcontext_worker.cpp \
b/refactoring/refactoringcontext_worker.cpp new file mode 100644
index 0000000..751d35b
--- /dev/null
+++ b/refactoring/refactoringcontext_worker.cpp
@@ -0,0 +1,68 @@
+/*
+    This file is part of KDevelop
+
+    Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "refactoringcontext.h" // NOTE: we have circular dependency here
+// Above must be included before...
+#include "refactoringcontext_worker.h"
+#include "refactoring.h"
+#include "refactoringmanager.h"
+#include "documentcache.h"
+
+RefactoringContext::Worker::Worker(
+    RefactoringContext *refactoringContext)
+    : QThread(nullptr)
+    , m_parent(refactoringContext)
+{
+#if(QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
+    qRegisterMetaType<std::function<void(clang::tooling::RefactoringTool &,
+                                         \
std::function<void(std::function<void()>)>)>>(); +    \
qRegisterMetaType<std::string>(); +#endif
+    moveToThread(this);
+    setObjectName("RefactoringContext - Worker");
+    connect(m_parent, &QObject::destroyed, this, [this]
+    {
+        m_parent = nullptr;
+        exit();
+    });
+}
+
+void RefactoringContext::Worker::invoke(
+    std::function<void(clang::tooling::RefactoringTool &,
+                       std::function<void(std::function<void()>)>)> task)
+{
+    task(m_parent->cache->refactoringTool(), [this](std::function<void()> \
resultCallback) +    {
+        emit taskFinished(resultCallback);
+    });
+}
+
+void RefactoringContext::Worker::invokeOnSingleFile(
+    std::function<void(clang::tooling::RefactoringTool &,
+                       std::function<void(std::function<void()>)>)> task,
+    const std::string &filename)
+{
+    auto tool = m_parent->cache->refactoringToolForFile(filename);
+    task(tool, [this](std::function<void()> resultCallback)
+    {
+        emit taskFinished(resultCallback);
+    });
+}
diff --git a/refactoring/refactoringcontext_worker.h \
b/refactoring/refactoringcontext_worker.h new file mode 100644
index 0000000..93ed446
--- /dev/null
+++ b/refactoring/refactoringcontext_worker.h
@@ -0,0 +1,63 @@
+/*
+    This file is part of KDevelop
+
+    Copyright 2015 Maciej Poleski <d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDEV_CLANG_REFACTORINGMANAGER_WORKER_H
+#define KDEV_CLANG_REFACTORINGMANAGER_WORKER_H
+
+// Clang
+#include <clang/Tooling/Refactoring.h>
+
+// Qt
+#include <QThread>
+
+#include "refactoringcontext.h"
+
+/**
+ * This is background worker for refactoring actions
+ */
+class RefactoringContext::Worker : public QThread
+{
+    Q_OBJECT;
+    Q_DISABLE_COPY(Worker);
+public:
+    Worker(RefactoringContext *parent);
+
+public slots:
+    void invoke(std::function<void(clang::tooling::RefactoringTool &,
+                                   std::function<void(std::function<void()>)>)> \
task); +    void invokeOnSingleFile(std::function<void(clang::tooling::RefactoringTool \
&, +                                               \
std::function<void(std::function<void()>)>)> task, +                            const \
std::string &filename); +
+signals:
+    void taskFinished(std::function<void()> resultCallback);
+
+private:
+    RefactoringContext *m_parent;
+};
+
+#if(QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
+Q_DECLARE_METATYPE(std::function<void(clang::tooling::RefactoringTool & ,
+                       std::function<void(std::function<void()>)>)>);
+Q_DECLARE_METATYPE(std::string);
+#endif
+
+#endif //KDEV_CLANG_REFACTORINGMANAGER_WORKER_H
diff --git a/refactoring/refactoringmanager.cpp b/refactoring/refactoringmanager.cpp
index e6b074e..6ab8ee2 100644
--- a/refactoring/refactoringmanager.cpp
+++ b/refactoring/refactoringmanager.cpp
@@ -27,6 +27,7 @@
 #include <clang/AST/RecursiveASTVisitor.h>
 #include <clang/Tooling/Tooling.h>
 
+#include "kdevrefactorings.h"
 #include "refactoringcontext.h"
 #include "documentcache.h"
 #include "utils.h"
@@ -158,31 +159,50 @@ private:
 
 }
 
-RefactoringManager::RefactoringManager(QObject *parent)
+#include "contextmenumutator.h"
+
+RefactoringManager::RefactoringManager(KDevRefactorings *parent)
     : QObject(parent)
 {
+    qRegisterMetaType<ContextMenuMutator *>();
+    qRegisterMetaType<QVector<Refactoring *>>();
+}
+
+KDevRefactorings *RefactoringManager::parent()
+{
+    return static_cast<KDevRefactorings *>(QObject::parent());
 }
 
-std::vector<Refactoring *> \
                RefactoringManager::allApplicableRefactorings(RefactoringContext \
                *ctx,
-                                                                         const QUrl \
                &sourceFile,
-                                                                         const \
KTextEditor::Cursor &location) +void \
RefactoringManager::fillContextMenu(KDevelop::ContextMenuExtension &extension, +      \
KDevelop::EditorContext *context)  {
-    const string filename = sourceFile.toLocalFile().toStdString();
-    auto clangTool = ctx->cache->refactoringToolForFile(filename);
+    const string filename = context->url().toLocalFile().toStdString();
     unsigned offset;
     {
-        auto _offset = ctx->offset(filename, location);
+        auto _offset = parent()->refactoringContext()->offset(filename, \
context->position());  if (!_offset) {
             // TODO: notify user
             refactorDebug() << "Unable to translate cursor position to offset in \
file:" <<  _offset.getError().message();
-            return {};
+            return;
         }
         offset = _offset.get();
     }
-    auto faf = cpp::make_unique<ExplorerActionFactory>(filename, offset);
-    clangTool.run(faf.get());
-    return std::move(faf->m_refactorings);
+    auto mutator = new ContextMenuMutator(extension, this);
+    QThread *mainThread = thread(); // Only for lambda below
+    parent()->refactoringContext()->scheduleOnSingleFile(
+        [filename, offset, mainThread](RefactoringTool &clangTool)
+        {
+            auto faf = cpp::make_unique<ExplorerActionFactory>(filename, offset);
+            clangTool.run(faf.get());
+            for (Refactoring *r : faf->m_refactorings) {
+                r->moveToThread(mainThread);
+            }
+            return QVector<Refactoring *>::fromStdVector(faf->m_refactorings);
+        }, filename, [mutator](const QVector<Refactoring *> &result)
+        {
+            mutator->endFillingContextMenu(result);
+        });
 }
 
 
diff --git a/refactoring/refactoringmanager.h b/refactoring/refactoringmanager.h
index cf0c919..87c685b 100644
--- a/refactoring/refactoringmanager.h
+++ b/refactoring/refactoringmanager.h
@@ -22,36 +22,38 @@
 #ifndef KDEV_CLANG_REFACTORINGMANAGER_H
 #define KDEV_CLANG_REFACTORINGMANAGER_H
 
-// base class
+// Qt
 #include <QObject>
+#include <QVector>
+
+// KDevelop
+#include <interfaces/contextmenuextension.h>
+#include <language/interfaces/editorcontext.h>
 
 // C++
-#include <vector>
 #include <memory>
 
 #include "refactoring.h"
 
+class KDevRefactorings;
+
 class RefactoringManager : public QObject
 {
     Q_OBJECT;
     Q_DISABLE_COPY(RefactoringManager);
+
 public:
-    RefactoringManager(QObject *parent);
-
-    /**
-     * Returns all applicable refactorings "here"
-     *
-     * @param rc Initialized refactorings context
-     * @param sourceFile queried source file name (full path)
-     * @param location offset from beginning of the file to location we are querying
-     *
-     * @return List of all applicable refactorings "here"
-     */
-    std::vector<Refactoring *> allApplicableRefactorings(RefactoringContext *ctx,
-                                                         const QUrl &sourceFile,
-                                                         const KTextEditor::Cursor \
                &location);
-    // or make RefactoringContext* property
+    RefactoringManager(KDevRefactorings *parent);
+
+    void fillContextMenu(KDevelop::ContextMenuExtension &extension,
+                         KDevelop::EditorContext *context);
+
+    KDevRefactorings *parent();
+
+private:
+
 };
 
+Q_DECLARE_METATYPE(QVector<Refactoring *>);
 
 #endif //KDEV_CLANG_REFACTORINGMANAGER_H


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic