diff --git a/languages/clang/CMakeLists.txt b/languages/clang/CMakeLists.txt --- a/languages/clang/CMakeLists.txt +++ b/languages/clang/CMakeLists.txt @@ -56,6 +56,7 @@ codegen/adaptsignatureassistant.cpp codegen/codegenhelper.cpp codegen/clangrefactoring.cpp + codegen/clangclasshelper.cpp codegen/sourcemanipulation.cpp duchain/builder.cpp diff --git a/languages/clang/clangsupport.h b/languages/clang/clangsupport.h --- a/languages/clang/clangsupport.h +++ b/languages/clang/clangsupport.h @@ -62,6 +62,7 @@ /** the code highlighter */ KDevelop::ICodeHighlighting* codeHighlighting() const override; KDevelop::BasicRefactoring* refactoring() const override; + KDevelop::ICreateClassHelper* createClassHelper() const override; void createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions) override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; diff --git a/languages/clang/clangsupport.cpp b/languages/clang/clangsupport.cpp --- a/languages/clang/clangsupport.cpp +++ b/languages/clang/clangsupport.cpp @@ -42,6 +42,7 @@ #include #include "codegen/clangrefactoring.h" +#include "codegen/clangclasshelper.h" #include "codegen/adaptsignatureassistant.h" #include "duchain/documentfinderhelpers.h" #include "duchain/clangindex.h" @@ -247,6 +248,11 @@ return m_refactoring; } +ICreateClassHelper* ClangSupport::createClassHelper() const +{ + return new ClangClassHelper; +} + ClangIndex* ClangSupport::index() { return m_index.data(); diff --git a/languages/clang/codegen/clangclasshelper.h b/languages/clang/codegen/clangclasshelper.h new file mode 100644 --- /dev/null +++ b/languages/clang/codegen/clangclasshelper.h @@ -0,0 +1,79 @@ +/* + * KDevelop C++ Language Support + * + * Copyright 2008 Hamish Rodda + * Copyright 2012 Miha Čančula + * + * This program 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 program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CLANGCLASSHELPER_H +#define CLANGCLASSHELPER_H + +#include "clangprivateexport.h" + +#include +#include + +class KDEVCLANGPRIVATE_EXPORT ClangClassHelper : public KDevelop::ICreateClassHelper +{ + +public: + ClangClassHelper(); + ~ClangClassHelper() override; + + KDevelop::TemplateClassGenerator* createGenerator(const QUrl& baseUrl) override; + QList< KDevelop::DeclarationPointer > defaultMethods(const QString& name) const override; +}; + +/** + * @brief A C++ specific template class generator + * + * This class adds additional variables to the template context. + * @sa extraVariables() + * + * Additionally, it attempts to add the class to a target after it is generated. + */ +class ClangTemplateNewClass : public KDevelop::TemplateClassGenerator +{ + public: + ClangTemplateNewClass(const QUrl& url); + ~ClangTemplateNewClass() override; + + KDevelop::DocumentChangeSet generate() override; + + /** + * In addition to the variables provided by the base class, + * it groups member variables and functions by access policy. + * The variables are of type VariableDescriptionList or FunctionDescriptionList + * and are called @c private_members, @c public_functions, etc. + * + * Signals and slots are not included in the above variables. Instead, they are listed in + * @c private_slots, @c protected_slots, @c public_slots and @c signals. + * + * For convenience, another variable named @c needs_qobject_macro is also provided. + * It is set to @c true if the class contains at least one signal or slot, and @c false otherwise. + * + * It also adds a @c namespaces variable which holds a list of all namespaces + * in which the class is nested. For a class identified by Foo::Bar::ExampleClass, + * @c namespaces holds two strings: "Foo" and "Bar". + */ + QVariantHash extraVariables(); + + void addBaseClass(const QString& base); +}; + +#endif // CLANGCLASSHELPER_H diff --git a/languages/clang/codegen/clangclasshelper.cpp b/languages/clang/codegen/clangclasshelper.cpp new file mode 100644 --- /dev/null +++ b/languages/clang/codegen/clangclasshelper.cpp @@ -0,0 +1,200 @@ +/* + * KDevelop C++ Language Support + * + * Copyright 2008 Hamish Rodda + * Copyright 2012 Miha Čančula + * Copyright 2017 Friedrich W. H. Kossebau + * + * This program 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 program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "clangclasshelper.h" + +#include "util/clangdebug.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace KDevelop; + +ClangClassHelper::ClangClassHelper() +{ +} + +ClangClassHelper::~ClangClassHelper() = default; + +TemplateClassGenerator* ClangClassHelper::createGenerator(const QUrl& baseUrl) +{ + return new ClangTemplateNewClass(baseUrl); +} + +QList ClangClassHelper::defaultMethods(const QString& name) const +{ + QTemporaryFile file(QDir::tempPath() + QLatin1String("/class_XXXXXX.cpp")); + file.setAutoRemove(false); + file.open(); + QTextStream stream(&file); + stream << "class " << name << " {\n" + << " public:\n" + // default ctor + << " " << name << "();\n" + // copy ctor + << " " << name << "(const " << name << "& other);\n" + // default dtor + << " ~" << name << "();\n" + // assignment operator + << " " << name << "& operator=(const " << name << "& other);\n" + // equality operator + << " bool operator==(const " << name << "& other) const;\n" + << "};\n"; + file.close(); + ReferencedTopDUContext context(DUChain::self()->waitForUpdate(IndexedString(file.fileName()), + TopDUContext::AllDeclarationsAndContexts)); + DUChainReadLocker lock; + + QList methods; + + if (context && context->childContexts().size() == 1) { + for (auto* declaration : context->childContexts().first()->localDeclarations()) { + methods << DeclarationPointer(declaration); + } + } + + file.remove(); + + return methods; +} + +ClangTemplateNewClass::ClangTemplateNewClass(const QUrl& url) + : TemplateClassGenerator(url) +{ +} + +ClangTemplateNewClass::~ClangTemplateNewClass() = default; + +QVariantHash ClangTemplateNewClass::extraVariables() +{ + QVariantHash variables; + + const QString publicAccess = QStringLiteral("public"); + + QMap variableDescriptions; + QMap functionDescriptions; + QMap slotDescriptions; + FunctionDescriptionList signalDescriptions; + + for (const auto& function : description().methods) { + const QString& access = function.access.isEmpty() ? publicAccess : function.access; + if (function.isSignal) { + signalDescriptions << function; + } + else if (function.isSlot) { + slotDescriptions[access] << function; + } + else { + functionDescriptions[access] << function; + } + } + + foreach (const VariableDescription& variable, description().members) { + const QString& access = variable.access.isEmpty() ? publicAccess : variable.access; + variableDescriptions[access] << variable; + } + + QMap::const_iterator vit, vend; + vit = variableDescriptions.constBegin(); + vend = variableDescriptions.constEnd(); + for (; vit != vend; ++vit) { + variables[vit.key() + QLatin1String("_members")] = CodeDescription::toVariantList(vit.value()); + } + + QMap::const_iterator fit, fend; + fit = functionDescriptions.constBegin(); + fend = functionDescriptions.constEnd(); + for (; fit != fend; ++fit) { + variables[fit.key() + QLatin1String("_functions")] = CodeDescription::toVariantList(fit.value()); + } + + fit = slotDescriptions.constBegin(); + fend = slotDescriptions.constEnd(); + for (; fit != fend; ++fit) { + variables[fit.key() + QLatin1String("_slots")] = CodeDescription::toVariantList(fit.value()); + } + + variables[QStringLiteral("signals")] = CodeDescription::toVariantList(signalDescriptions); + variables[QStringLiteral("needs_qobject_macro")] = !slotDescriptions.isEmpty() || !signalDescriptions.isEmpty(); + + // TODO: port to KDev-clang +#if 0 + QStringList includedFiles; + DUChainReadLocker locker(DUChain::lock()); + + QUrl sourceUrl; + QHash urls = fileUrls(); + if (!urls.isEmpty()) { + sourceUrl = urls.constBegin().value(); + } + else { + // includeDirectiveFromUrl() expects a header URL + sourceUrl = baseUrl(); + sourceUrl.setPath(sourceUrl.path() + '/' + QLatin1String(".h")); + } + + for (const auto& base : directBaseClasses()) { + if (!base) { + continue; + } + + clangDebug() << "Looking for includes for class" << base->identifier().toString(); + QExplicitlySharedDataPointer item = Cpp::includeDirectiveFromUrl(sourceUrl, IndexedDeclaration(base.data())); + if(item) + { + clangDebug() << "Found one in" << item->m_canonicalPath; + includedFiles << item->m_addedInclude; + } + } + variables[QStringLiteral("included_files")] = includedFiles; +#endif + + return variables; +} + + +DocumentChangeSet ClangTemplateNewClass::generate() +{ + addVariables(extraVariables()); + return TemplateClassGenerator::generate(); +} + +void ClangTemplateNewClass::addBaseClass(const QString& base) +{ + // strip access specifier + QStringList splitBase = base.split(QLatin1Char(' '), QString::SkipEmptyParts); + + //if no access specifier is found use public by default + if (splitBase.size() == 1) { + splitBase.prepend(QStringLiteral("public")); + } + + // Call base function with the access specifier + TemplateClassGenerator::addBaseClass(splitBase.join(QStringLiteral(" "))); +} diff --git a/languages/clang/kdevclangsupport.json b/languages/clang/kdevclangsupport.json --- a/languages/clang/kdevclangsupport.json +++ b/languages/clang/kdevclangsupport.json @@ -54,7 +54,7 @@ "X-KDevelop-Interfaces": [ "ILanguageSupport" ], - "X-KDevelop-Language": "C", + "X-KDevelop-Language": ["C", "C++"], "X-KDevelop-LoadMode": "AlwaysOn", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-SupportedMimeTypes": [