diff --git a/documentation/manpage/CMakeLists.txt b/documentation/manpage/CMakeLists.txt index e99a0fb162..067579c00e 100644 --- a/documentation/manpage/CMakeLists.txt +++ b/documentation/manpage/CMakeLists.txt @@ -1,18 +1,18 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevmanpage\") set(kdevmanpage_SRCS manpagedocumentation.cpp manpageplugin.cpp manpagemodel.cpp manpagedocumentationwidget.cpp ) kdevplatform_add_plugin(kdevmanpage JSON kdevmanpage.json SOURCES ${kdevmanpage_SRCS}) target_link_libraries(kdevmanpage KF5::TextEditor KDev::Language -KDev::Documentation KDev::Interfaces KF5::KCMUtils Qt5::WebKitWidgets) +KDev::Documentation KDev::Interfaces KF5::KCMUtils) install(FILES manpagedocumentation.css DESTINATION ${DATA_INSTALL_DIR}/kdevmanpage ) add_subdirectory(tests) diff --git a/documentation/qthelp/CMakeLists.txt b/documentation/qthelp/CMakeLists.txt index 788fd0cf56..eda3c082d5 100644 --- a/documentation/qthelp/CMakeLists.txt +++ b/documentation/qthelp/CMakeLists.txt @@ -1,25 +1,25 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevqthelp\") set(kdevqthelp_SRCS qthelpplugin.cpp qthelpproviderabstract.cpp qthelpprovider.cpp qthelpdocumentation.cpp qthelpqtdoc.cpp qthelp_config_shared.cpp debug.cpp qthelpconfig.cpp # Configuration module for QtHelp plugin ) ki18n_wrap_ui(kdevqthelp_SRCS qthelpconfig.ui qthelpconfigeditdialog.ui ) qt5_add_resources(kdevqthelp_SRCS kdevqthelp.qrc) kdevplatform_add_plugin(kdevqthelp JSON kdevqthelp.json SOURCES ${kdevqthelp_SRCS}) target_link_libraries(kdevqthelp - KF5::KCMUtils KF5::I18n KF5::KIOWidgets KF5::TextEditor KF5::IconThemes Qt5::Help Qt5::WebKitWidgets KF5::NewStuff + KF5::KCMUtils KF5::I18n KF5::KIOWidgets KF5::TextEditor KF5::IconThemes Qt5::Help KF5::NewStuff KDev::Language KDev::Documentation KDev::Interfaces) add_subdirectory(tests) diff --git a/documentation/qthelp/qthelpconfig.cpp b/documentation/qthelp/qthelpconfig.cpp index 12ca45d41b..d134bba42b 100644 --- a/documentation/qthelp/qthelpconfig.cpp +++ b/documentation/qthelp/qthelpconfig.cpp @@ -1,359 +1,351 @@ /* This file is part of KDevelop Copyright 2010 Benjamin Port Copyright 2014 Kevin Funk Copyright 2016 Andreas Cord-Landwehr 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 "qthelpconfig.h" #include #include #include #include #include #include #include #include #include "ui_qthelpconfig.h" #include "ui_qthelpconfigeditdialog.h" #include "qthelp_config_shared.h" #include "debug.h" #include "qthelpplugin.h" enum Column { NameColumn, PathColumn, IconColumn, GhnsColumn, ConfigColumn }; class QtHelpConfigEditDialog : public QDialog, public Ui_QtHelpConfigEditDialog { public: explicit QtHelpConfigEditDialog(QTreeWidgetItem* modifiedItem, QtHelpConfig* parent = nullptr, Qt::WindowFlags f = nullptr) : QDialog(parent, f) , m_modifiedItem(modifiedItem) , m_config(parent) { setupUi(this); if (modifiedItem) { setWindowTitle(i18n("Modify Entry")); } else { setWindowTitle(i18n("Add New Entry")); } qchIcon->setIcon("qtlogo"); } bool checkQtHelpFile(); void accept() override; private: QTreeWidgetItem* m_modifiedItem; QtHelpConfig* m_config; }; bool QtHelpConfigEditDialog::checkQtHelpFile() { //verify if the file is valid and if there is a name if(qchName->text().isEmpty()){ KMessageBox::error(this, i18n("Name cannot be empty.")); return false; } return m_config->checkNamespace(qchRequester->text(), m_modifiedItem); } void QtHelpConfigEditDialog::accept() { if (!checkQtHelpFile()) return; QDialog::accept(); } QtHelpConfig::QtHelpConfig(QtHelpPlugin* plugin, QWidget *parent) : KDevelop::ConfigPage(plugin, nullptr, parent) { QVBoxLayout * l = new QVBoxLayout( this ); QWidget* w = new QWidget; m_configWidget = new Ui::QtHelpConfigUI; m_configWidget->setupUi( w ); m_configWidget->addButton->setIcon(QIcon::fromTheme("list-add")); connect(m_configWidget->addButton, &QPushButton::clicked, this, &QtHelpConfig::add); // Table m_configWidget->qchTable->setColumnHidden(IconColumn, true); m_configWidget->qchTable->setColumnHidden(GhnsColumn, true); m_configWidget->qchTable->model()->setHeaderData(ConfigColumn, Qt::Horizontal, QVariant()); m_configWidget->qchTable->header()->setSectionsMovable(false); m_configWidget->qchTable->header()->setStretchLastSection(false); m_configWidget->qchTable->header()->setSectionResizeMode(NameColumn, QHeaderView::Stretch); m_configWidget->qchTable->header()->setSectionResizeMode(PathColumn, QHeaderView::Stretch); m_configWidget->qchTable->header()->setSectionResizeMode(ConfigColumn, QHeaderView::Fixed); // Add GHNS button KNS3::Button *knsButton = new KNS3::Button(i18nc("Allow user to get some API documentation with GHNS", "Get New Documentation"), "kdevelop-qthelp.knsrc", m_configWidget->boxQchManage); m_configWidget->tableCtrlLayout->insertWidget(1, knsButton); connect(knsButton, &KNS3::Button::dialogFinished, this, &QtHelpConfig::knsUpdate); - connect(m_configWidget->loadQtDocsCheckBox, &QCheckBox::toggled, this, static_cast(&QtHelpConfig::changed)); - connect(m_configWidget->qchSearchDirButton, &QPushButton::clicked, this, &QtHelpConfig::chooseSearchDir); - connect(m_configWidget->qchSearchDir,&QLineEdit::textChanged, this, &QtHelpConfig::searchDirChanged); + connect(m_configWidget->loadQtDocsCheckBox, &QCheckBox::toggled, + this, static_cast(&QtHelpConfig::changed)); + m_configWidget->qchSearchDir->setMode(KFile::Directory); + connect(m_configWidget->qchSearchDir, &KUrlRequester::textChanged, + this, &QtHelpConfig::changed); // Set availability information for QtHelp m_configWidget->messageAvailabilityQtDocs->setCloseButtonVisible(false); if(plugin->isQtHelpAvailable()) { m_configWidget->messageAvailabilityQtDocs->setVisible(false); } else { m_configWidget->messageAvailabilityQtDocs->setText( i18n("The command \"qmake -query\" could not provide a path to a QtHelp file (QCH).")); m_configWidget->loadQtDocsCheckBox->setVisible(false); } l->addWidget(w); reset(); } QtHelpConfig::~QtHelpConfig() { delete m_configWidget; } void QtHelpConfig::apply() { QStringList iconList, nameList, pathList, ghnsList; for (int i = 0; i < m_configWidget->qchTable->topLevelItemCount(); i++) { const QTreeWidgetItem* item = m_configWidget->qchTable->topLevelItem(i); nameList << item->text(0); pathList << item->text(1); iconList << item->text(2); ghnsList << item->text(3); } QString searchDir = m_configWidget->qchSearchDir->text(); bool loadQtDoc = m_configWidget->loadQtDocsCheckBox->isChecked(); qtHelpWriteConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc); static_cast(plugin())->readConfig(); } void QtHelpConfig::reset() { m_configWidget->qchTable->clear(); QStringList iconList, nameList, pathList, ghnsList; QString searchDir; bool loadQtDoc; qtHelpReadConfig(iconList, nameList, pathList, ghnsList, searchDir, loadQtDoc); const int size = qMin(qMin(iconList.size(), nameList.size()), pathList.size()); for(int i = 0; i < size; ++i) { QString ghnsStatus = ghnsList.size()>i ? ghnsList.at(i) : "0"; addTableItem(iconList.at(i), nameList.at(i), pathList.at(i), ghnsStatus); } m_configWidget->qchSearchDir->setText(searchDir); m_configWidget->loadQtDocsCheckBox->setChecked(loadQtDoc); emit changed(); } void QtHelpConfig::defaults() { bool change = false; if(m_configWidget->qchTable->topLevelItemCount() > 0) { m_configWidget->qchTable->clear(); change = true; } if(!m_configWidget->loadQtDocsCheckBox->isChecked()){ m_configWidget->loadQtDocsCheckBox->setChecked(true); change = true; } if (change) { emit changed(); } } void QtHelpConfig::add() { QtHelpConfigEditDialog dialog(nullptr, this); if (!dialog.exec()) return; QTreeWidgetItem* item = addTableItem(dialog.qchIcon->icon(), dialog.qchName->text(), dialog.qchRequester->text(), "0"); m_configWidget->qchTable->setCurrentItem(item); emit changed(); } void QtHelpConfig::modify(QTreeWidgetItem* item) { if (!item) return; QtHelpConfigEditDialog dialog(item, this); if (item->text(GhnsColumn) != "0") { dialog.qchRequester->setText(i18n("Documentation provided by GHNS")); dialog.qchRequester->setEnabled(false); } else { dialog.qchRequester->setText(item->text(PathColumn)); dialog.qchRequester->setEnabled(true); } dialog.qchName->setText(item->text(NameColumn)); dialog.qchIcon->setIcon(item->text(IconColumn)); if (!dialog.exec()) { return; } item->setIcon(NameColumn, QIcon(dialog.qchIcon->icon())); item->setText(NameColumn, dialog.qchName->text()); item->setText(IconColumn, dialog.qchIcon->icon()); if(item->text(GhnsColumn) == "0") { item->setText(PathColumn, dialog.qchRequester->text()); } emit changed(); } bool QtHelpConfig::checkNamespace(const QString& filename, QTreeWidgetItem* modifiedItem) { QString qtHelpNamespace = QHelpEngineCore::namespaceName(filename); if (qtHelpNamespace.isEmpty()) { // Open error message (not valid Qt Compressed Help file) KMessageBox::error(this, i18n("Qt Compressed Help file is not valid.")); return false; } // verify if it's the namespace it's not already in the list for(int i=0; i < m_configWidget->qchTable->topLevelItemCount(); i++) { const QTreeWidgetItem* item = m_configWidget->qchTable->topLevelItem(i); if (item != modifiedItem){ if (qtHelpNamespace == QHelpEngineCore::namespaceName(item->text(PathColumn))) { // Open error message, documentation already imported KMessageBox::error(this, i18n("Documentation already imported")); return false; } } } return true; } void QtHelpConfig::remove(QTreeWidgetItem* item) { if (!item) return; delete item; emit changed(); } void QtHelpConfig::knsUpdate(KNS3::Entry::List list) { if (list.isEmpty()) return; foreach (const KNS3::Entry& e, list) { if(e.status() == KNS3::Entry::Installed) { if(e.installedFiles().size() == 1) { QString filename = e.installedFiles().at(0); if(checkNamespace(filename, nullptr)){ QTreeWidgetItem* item = addTableItem("documentation", e.name(), filename, "1"); m_configWidget->qchTable->setCurrentItem(item); } else { qCDebug(QTHELP) << "namespace error"; } } } else if(e.status() == KNS3::Entry::Deleted) { if(e.uninstalledFiles().size() == 1) { for(int i=0; i < m_configWidget->qchTable->topLevelItemCount(); i++) { QTreeWidgetItem* item = m_configWidget->qchTable->topLevelItem(i); if (e.uninstalledFiles().at(0) == item->text(PathColumn)) { delete item; break; } } } } } emit changed(); } -void QtHelpConfig::chooseSearchDir() -{ - m_configWidget->qchSearchDir->setText(QFileDialog::getExistingDirectory(this)); -} - -void QtHelpConfig::searchDirChanged() -{ - emit changed(); -} - QString QtHelpConfig::name() const { return i18n("QtHelp Documentation"); } QIcon QtHelpConfig::icon() const { return QIcon::fromTheme(QStringLiteral("help-contents")); } QTreeWidgetItem * QtHelpConfig::addTableItem(const QString &icon, const QString &name, const QString &path, const QString &ghnsStatus) { QTreeWidgetItem *item = new QTreeWidgetItem(m_configWidget->qchTable); item->setIcon(NameColumn, QIcon::fromTheme(icon)); item->setText(NameColumn, name); item->setToolTip(NameColumn, name); item->setText(PathColumn, path); item->setToolTip(PathColumn, path); item->setText(IconColumn, icon); item->setText(GhnsColumn, ghnsStatus); QWidget *ctrlWidget = new QWidget(item->treeWidget()); ctrlWidget->setLayout(new QHBoxLayout(ctrlWidget)); QToolButton *modifyBtn = new QToolButton(item->treeWidget()); modifyBtn->setIcon(QIcon::fromTheme("document-edit")); modifyBtn->setToolTip(ki18n("Modify").toString()); connect(modifyBtn, &QPushButton::clicked, this, [=](){ modify(item); }); QToolButton *removeBtn = new QToolButton(item->treeWidget()); removeBtn->setIcon(QIcon::fromTheme("entry-delete")); removeBtn->setToolTip(ki18n("Delete").toString()); if (item->text(GhnsColumn) != "0") { // KNS3 currently does not provide API to uninstall entries // just removing the files results in wrong installed states in the KNS3 dialog // TODO: add API to KNS to remove files without UI interaction removeBtn->setEnabled(false); removeBtn->setToolTip(tr("Please uninstall this via GHNS")); } else { connect(removeBtn, &QPushButton::clicked, this, [=](){ remove(item); }); } ctrlWidget->layout()->addWidget(modifyBtn); ctrlWidget->layout()->addWidget(removeBtn); m_configWidget->qchTable->setItemWidget(item, ConfigColumn, ctrlWidget); return item; } diff --git a/documentation/qthelp/qthelpconfig.h b/documentation/qthelp/qthelpconfig.h index cdd88b75ae..982f744be0 100644 --- a/documentation/qthelp/qthelpconfig.h +++ b/documentation/qthelp/qthelpconfig.h @@ -1,69 +1,67 @@ /* This file is part of KDevelop Copyright 2010 Benjamin Port Copyright 2014 Kevin Funk 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 QTHELPCONFIG_H #define QTHELPCONFIG_H #include #include class QTreeWidgetItem; class QtHelpPlugin; namespace Ui { class QtHelpConfigUI; } class QtHelpConfig : public KDevelop::ConfigPage { public: Q_OBJECT public: explicit QtHelpConfig(QtHelpPlugin* plugin, QWidget *parent = nullptr); ~QtHelpConfig() override; bool checkNamespace(const QString &filename, QTreeWidgetItem* modifiedItem); QString name() const override; QIcon icon() const override; private slots: void add(); void remove(QTreeWidgetItem* item); void modify(QTreeWidgetItem* item); void knsUpdate(KNS3::Entry::List list); - void chooseSearchDir(); - void searchDirChanged(); public Q_SLOTS: void apply() override; void defaults() override; void reset() override; private: QTreeWidgetItem * addTableItem(const QString &icon, const QString &name, const QString &path, const QString &ghnsStatus); Ui::QtHelpConfigUI* m_configWidget; }; #endif // QTHELPCONFIG_H diff --git a/documentation/qthelp/qthelpconfig.ui b/documentation/qthelp/qthelpconfig.ui index 6e882b194b..8886c97859 100644 --- a/documentation/qthelp/qthelpconfig.ui +++ b/documentation/qthelp/qthelpconfig.ui @@ -1,251 +1,200 @@ QtHelpConfigUI 0 0 560 443 Load QtHelp Documentation from System Locations - + - Load Qt API documentation: + &Load Qt API documentation: loadQtDocsCheckBox - + + + + + + + + Load QtHelp files from directory: - - qchSearchDir - - - - - - - - 0 - - - - - true - - - - 0 - 0 - - - - - - - - - - - - - - - - - - + + - - - - - 0 - 0 - + + + + true 0 0 - - - - - - - - Search QtHelp files in this directory... - - - - - - - - - - - - - - - - - - - - + + + Install Additional Documentation Files 1 0 QAbstractItemView::NoEditTriggers false false 5 false true Name Path IconName Ghns Add Qt::Horizontal 40 20 0 Qt::Vertical 20 40 KMessageWidget QWidget -
kmessagewidget.h
+
kmessagewidget.h
+
+ + KUrlRequester + QWidget +
kurlrequester.h
+ 1
diff --git a/documentation/qthelp/qthelpproviderabstract.cpp b/documentation/qthelp/qthelpproviderabstract.cpp index a115aca190..7f67fa47d7 100644 --- a/documentation/qthelp/qthelpproviderabstract.cpp +++ b/documentation/qthelp/qthelpproviderabstract.cpp @@ -1,112 +1,117 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol Copyright 2009 David Nolden Copyright 2010 Benjamin Port 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 "qthelpprovider.h" #include #include #include #include #include #include #include #include #include "qthelpdocumentation.h" #include "debug.h" using namespace KDevelop; QtHelpProviderAbstract::QtHelpProviderAbstract(QObject *parent, const QString &collectionFileName, const QVariantList &args) : QObject(parent) , m_engine(QStandardPaths::writableLocation(QStandardPaths::DataLocation)+'/'+collectionFileName) { Q_UNUSED(args); if( !m_engine.setupData() ) { qWarning() << "Couldn't setup QtHelp Collection file"; } } + +QtHelpProviderAbstract::~QtHelpProviderAbstract() +{ +} + IDocumentation::Ptr QtHelpProviderAbstract::documentationForDeclaration(Declaration* dec) const { QtHelpDocumentation::s_provider = const_cast(this); if(dec) { static const IndexedString qmlJs("QML/JS"); bool isQML; QStringList idParts; { DUChainReadLocker lock; isQML = dec->topContext()->parsingEnvironmentFile()->language() == qmlJs; idParts = dec->qualifiedIdentifier().toStringList(); } QString id; if(isQML && !idParts.isEmpty()) { id = QLatin1String("QML."); } if(!idParts.isEmpty()) { id += idParts.join(QLatin1String("::")); QMap links=m_engine.linksForIdentifier(id); if(!links.isEmpty()) return IDocumentation::Ptr(new QtHelpDocumentation(id, links)); } } return {}; } QAbstractListModel* QtHelpProviderAbstract::indexModel() const { QtHelpDocumentation::s_provider = const_cast(this); return m_engine.indexModel(); } IDocumentation::Ptr QtHelpProviderAbstract::documentationForIndex(const QModelIndex& idx) const { QtHelpDocumentation::s_provider = const_cast(this); QString name=idx.data(Qt::DisplayRole).toString(); return IDocumentation::Ptr(new QtHelpDocumentation(name, m_engine.indexModel()->linksForKeyword(name))); } void QtHelpProviderAbstract::jumpedTo(const QUrl& newUrl) const { QtHelpDocumentation::s_provider = const_cast(this); QMap info; info.insert(newUrl.toString(), newUrl); IDocumentation::Ptr doc(new QtHelpDocumentation(newUrl.toString(), info)); emit addHistory(doc); } IDocumentation::Ptr QtHelpProviderAbstract::homePage() const { QtHelpDocumentation::s_provider = const_cast(this); return IDocumentation::Ptr(new HomeDocumentation); } bool QtHelpProviderAbstract::isValid() const { return !m_engine.registeredDocumentations().isEmpty(); } diff --git a/documentation/qthelp/qthelpproviderabstract.h b/documentation/qthelp/qthelpproviderabstract.h index 550b4cabcb..1f5f1b28fb 100644 --- a/documentation/qthelp/qthelpproviderabstract.h +++ b/documentation/qthelp/qthelpproviderabstract.h @@ -1,58 +1,58 @@ /* This file is part of KDevelop Copyright 2009 Aleix Pol Copyright 2009 David Nolden Copyright 2010 Benjamin Port 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 QTHELPPROVIDERABSTRACT_H #define QTHELPPROVIDERABSTRACT_H #include #include #include #include #include #include - class QtHelpProviderAbstract : public QObject, public KDevelop::IDocumentationProvider { Q_OBJECT Q_INTERFACES( KDevelop::IDocumentationProvider ) public: QtHelpProviderAbstract(QObject *parent, const QString &collectionFileName, const QVariantList & args); + virtual ~QtHelpProviderAbstract(); KDevelop::IDocumentation::Ptr documentationForDeclaration (KDevelop::Declaration*) const override; KDevelop::IDocumentation::Ptr documentationForIndex(const QModelIndex& idx) const override; QAbstractListModel* indexModel() const override; KDevelop::IDocumentation::Ptr homePage() const override; /// @return False in case we failed to load any documentation files, else true bool isValid() const; QHelpEngine* engine() { return &m_engine; } public slots: void jumpedTo(const QUrl& newUrl) const; signals: void addHistory(const KDevelop::IDocumentation::Ptr& doc) const override; protected: QHelpEngine m_engine; }; #endif // QTHELPPROVIDERABSTRACT_H diff --git a/documentation/qthelp/tests/CMakeLists.txt b/documentation/qthelp/tests/CMakeLists.txt index 38b7953ebe..d33b392771 100644 --- a/documentation/qthelp/tests/CMakeLists.txt +++ b/documentation/qthelp/tests/CMakeLists.txt @@ -1,23 +1,23 @@ configure_file(testqthelpconfig.h.in testqthelpconfig.h) set(test_qthelpplugin_SRCS test_qthelpplugin.cpp ../qthelpplugin.cpp ../qthelpproviderabstract.cpp ../qthelpprovider.cpp ../qthelpdocumentation.cpp ../qthelpqtdoc.cpp ../qthelp_config_shared.cpp ../debug.cpp ../qthelpconfig.cpp ) ki18n_wrap_ui(test_qthelpplugin_SRCS ../qthelpconfig.ui ../qthelpconfigeditdialog.ui ) ecm_add_test(${test_qthelpplugin_SRCS} TEST_NAME test_qthelpplugin - LINK_LIBRARIES Qt5::Test KF5::NewStuff KF5::KIOWidgets KF5::TextEditor KF5::IconThemes Qt5::Help Qt5::WebKitWidgets KDev::Tests KDev::Documentation + LINK_LIBRARIES Qt5::Test KF5::NewStuff KF5::KIOWidgets KF5::TextEditor KF5::IconThemes Qt5::Help KDev::Tests KDev::Documentation ) diff --git a/languages/clang/duchain/builder.cpp b/languages/clang/duchain/builder.cpp index 7a58e3222d..22bafd9f36 100644 --- a/languages/clang/duchain/builder.cpp +++ b/languages/clang/duchain/builder.cpp @@ -1,1519 +1,1519 @@ /* * This file is part of KDevelop * * Copyright 2013 Olivier de Gaalon * Copyright 2015 Milian Wolff * * 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 "builder.h" #include "util/clangdebug.h" #include "templatehelpers.h" #include "cursorkindtraits.h" #include "clangducontext.h" #include "macrodefinition.h" #include "types/classspecializationtype.h" #include "util/clangdebug.h" #include "util/clangutils.h" #include "util/clangtypes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// Turn on for debugging the declaration building #define IF_DEBUG(x) namespace { // TODO: this is ugly, can we find a better alternative? bool jsonTestRun() { static bool runningTest = qEnvironmentVariableIsSet("KDEV_CLANG_JSON_TEST_RUN"); return runningTest; } //BEGIN helpers /** * Find the cursor that cursor @p cursor references * * First tries to get the referenced cursor via clang_getCursorReferenced, * and if that fails, tries to get them via clang_getOverloadedDecl * (which returns the referenced cursor for CXCursor_OverloadedDeclRef, for example) * * @return Valid cursor on success, else null cursor */ CXCursor referencedCursor(CXCursor cursor) { auto referenced = clang_getCursorReferenced(cursor); if (!clang_equalCursors(cursor, referenced)) { return referenced; } // get the first result for now referenced = clang_getOverloadedDecl(cursor, 0); if (!clang_Cursor_isNull(referenced)) { return referenced; } return clang_getNullCursor(); } Identifier makeId(CXCursor cursor) { if (CursorKindTraits::isClassTemplate(cursor.kind)) { // TODO: how to handle functions here? We don't want to add the "real" function arguments here // and there does not seem to be an API to get the template arguments for non-specializations easily // NOTE: using the QString overload of the Identifier ctor here, so that the template name gets parsed return Identifier(ClangString(clang_getCursorDisplayName(cursor)).toString()); } return Identifier(ClangString(clang_getCursorSpelling(cursor)).toIndexed()); } QByteArray makeComment(CXComment comment) { if (Q_UNLIKELY(jsonTestRun())) { auto kind = clang_Comment_getKind(comment); if (kind == CXComment_Text) return ClangString(clang_TextComment_getText(comment)).toByteArray(); QByteArray text; int numChildren = clang_Comment_getNumChildren(comment); for (int i = 0; i < numChildren; ++i) text += makeComment(clang_Comment_getChild(comment, i)); return text; } return ClangString(clang_FullComment_getAsHTML(comment)).toByteArray(); } AbstractType* createDelayedType(CXType type) { auto t = new DelayedType; QString typeName = ClangString(clang_getTypeSpelling(type)).toString(); typeName.remove(QStringLiteral("const ")); typeName.remove(QStringLiteral("volatile ")); t->setIdentifier(IndexedTypeIdentifier(typeName)); return t; } void contextImportDecl(DUContext* context, const DeclarationPointer& decl) { auto top = context->topContext(); if (auto import = decl->logicalInternalContext(top)) { context->addImportedParentContext(import); context->topContext()->updateImportsCache(); } } //END helpers CXChildVisitResult visitCursor(CXCursor cursor, CXCursor parent, CXClientData data); //BEGIN IdType template struct IdType; template struct IdType::type> { typedef StructureType Type; }; template struct IdType::type> { typedef TypeAliasType Type; }; template struct IdType::type> { typedef TypeAliasType Type; }; template struct IdType::type> { typedef EnumerationType Type; }; template struct IdType::type> { typedef EnumeratorType Type; }; //END IdType //BEGIN DeclType template struct DeclType; template struct DeclType::type> { typedef Declaration Type; }; template struct DeclType::type> { typedef MacroDefinition Type; }; template struct DeclType::type> { typedef ForwardDeclaration Type; }; template struct DeclType::type> { typedef ClassDeclaration Type; }; template struct DeclType::type> { typedef ClassFunctionDeclaration Type; }; template struct DeclType::type> { typedef FunctionDeclaration Type; }; template struct DeclType::type> { typedef FunctionDefinition Type; }; template struct DeclType::type> { typedef NamespaceAliasDeclaration Type; }; template struct DeclType::type> { typedef ClassMemberDeclaration Type; }; //END DeclType //BEGIN CurrentContext struct CurrentContext { CurrentContext(DUContext* context, QSet keepAliveContexts) : context(context) , keepAliveContexts(keepAliveContexts) { DUChainReadLocker lock; previousChildContexts = context->childContexts(); previousChildDeclarations = context->localDeclarations(); } ~CurrentContext() { DUChainWriteLocker lock; foreach (auto childContext, previousChildContexts) { if (!keepAliveContexts.contains(childContext)) { delete childContext; } } qDeleteAll(previousChildDeclarations); if (resortChildContexts) { context->resortChildContexts(); } if (resortLocalDeclarations) { context->resortLocalDeclarations(); } } DUContext* context; // when updating, this contains child contexts of the current parent context QVector previousChildContexts; // when updating, this contains contexts that must not be deleted QSet keepAliveContexts; // when updating, this contains child declarations of the current parent context QVector previousChildDeclarations; bool resortChildContexts = false; bool resortLocalDeclarations = false; }; //END CurrentContext //BEGIN Visitor struct Visitor { explicit Visitor(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update); AbstractType *makeType(CXType type, CXCursor parent); AbstractType::Ptr makeAbsType(CXType type, CXCursor parent) { return AbstractType::Ptr(makeType(type, parent)); } //BEGIN dispatch* template = dummy> CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent); template = dummy> CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent); template = dummy> CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent); template AbstractType *dispatchType(CXType type, CXCursor cursor) { IF_DEBUG(clangDebug() << "TK:" << type.kind;) auto kdevType = createType(type, cursor); if (kdevType) { setTypeModifiers(type, kdevType); } return kdevType; } //BEGIN dispatch* //BEGIN build* template CXChildVisitResult buildDeclaration(CXCursor cursor); CXChildVisitResult buildUse(CXCursor cursor); CXChildVisitResult buildMacroExpansion(CXCursor cursor); CXChildVisitResult buildCompoundStatement(CXCursor cursor); CXChildVisitResult buildCXXBaseSpecifier(CXCursor cursor); CXChildVisitResult buildParmDecl(CXCursor cursor); //END build* //BEGIN create* template DeclType* createDeclarationCommon(CXCursor cursor, const Identifier& id) { auto range = ClangHelpers::cursorSpellingNameRange(cursor, id); if (id.isEmpty()) { // This is either an anonymous function parameter e.g.: void f(int); // Or anonymous struct/class/union e.g.: struct {} anonymous; // Set empty range for it range.end = range.start; } // check if cursor is inside a macro expansion auto clangRange = clang_Cursor_getSpellingNameRange(cursor, 0, 0); unsigned int expansionLocOffset; const auto spellingLocation = clang_getRangeStart(clangRange); clang_getExpansionLocation(spellingLocation, nullptr, nullptr, nullptr, &expansionLocOffset); if (m_macroExpansionLocations.contains(expansionLocOffset)) { unsigned int spellingLocOffset; clang_getSpellingLocation(spellingLocation, nullptr, nullptr, nullptr, &spellingLocOffset); // Set empty ranges for declarations inside macro expansion if (spellingLocOffset == expansionLocOffset) { range.end = range.start; } } if (m_update) { const IndexedIdentifier indexedId(id); DUChainWriteLocker lock; auto it = m_parentContext->previousChildDeclarations.begin(); while (it != m_parentContext->previousChildDeclarations.end()) { auto decl = dynamic_cast(*it); if (decl && decl->indexedIdentifier() == indexedId) { decl->setRange(range); m_parentContext->resortLocalDeclarations = true; setDeclData(cursor, decl); m_cursorToDeclarationCache[cursor] = decl; m_parentContext->previousChildDeclarations.erase(it); return decl; } ++it; } } auto decl = new DeclType(range, nullptr); decl->setIdentifier(id); m_cursorToDeclarationCache[cursor] = decl; setDeclData(cursor, decl); { DUChainWriteLocker lock; decl->setContext(m_parentContext->context); } return decl; } template Declaration* createDeclaration(CXCursor cursor, const Identifier& id, DUContext *context) { auto decl = createDeclarationCommon(cursor, id); auto type = createType(cursor); DUChainWriteLocker lock; if (context) decl->setInternalContext(context); setDeclType(decl, type); setDeclInCtxtData(cursor, decl); return decl; } template DUContext* createContext(CXCursor cursor, const QualifiedIdentifier& scopeId = {}) { // wtf: why is the DUContext API requesting a QID when it needs a plain Id?! // see: testNamespace auto range = ClangRange(clang_getCursorExtent(cursor)).toRangeInRevision(); DUChainWriteLocker lock; if (m_update) { const IndexedQualifiedIdentifier indexedScopeId(scopeId); auto it = m_parentContext->previousChildContexts.begin(); while (it != m_parentContext->previousChildContexts.end()) { auto ctx = *it; if (ctx->type() == Type && ctx->indexedLocalScopeIdentifier() == indexedScopeId) { ctx->setRange(range); m_parentContext->resortChildContexts = true; m_parentContext->previousChildContexts.erase(it); return ctx; } ++it; } } //TODO: (..type, id..) constructor for DUContext? auto context = new ClangNormalDUContext(range, m_parentContext->context); context->setType(Type); context->setLocalScopeIdentifier(scopeId); if (Type == DUContext::Other || Type == DUContext::Function) context->setInSymbolTable(false); if (CK == CXCursor_CXXMethod) { CXCursor semParent = clang_getCursorSemanticParent(cursor); // only import the semantic parent if it differs from the lexical parent if (!clang_Cursor_isNull(semParent) && !clang_equalCursors(semParent, clang_getCursorLexicalParent(cursor))) { auto semParentDecl = findDeclaration(semParent); if (semParentDecl) { contextImportDecl(context, semParentDecl); } } } return context; } template = dummy> AbstractType *createType(CXType, CXCursor) { // TODO: would be nice to instantiate a ConstantIntegralType here and set a value if possible // but unfortunately libclang doesn't offer API to that // also see http://marc.info/?l=cfe-commits&m=131609142917881&w=2 return new IntegralType(CursorKindTraits::integralType(TK)); } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto ptr = new PointerType; ptr->setBaseType(makeAbsType(clang_getPointeeType(type), parent)); return ptr; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto arr = new ArrayType; arr->setDimension((TK == CXType_IncompleteArray || TK == CXType_VariableArray || TK == CXType_DependentSizedArray) ? 0 : clang_getArraySize(type)); arr->setElementType(makeAbsType(clang_getArrayElementType(type), parent)); return arr; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto ref = new ReferenceType; ref->setIsRValue(type.kind == CXType_RValueReference); ref->setBaseType(makeAbsType(clang_getPointeeType(type), parent)); return ref; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto func = new FunctionType; func->setReturnType(makeAbsType(clang_getResultType(type), parent)); const int numArgs = clang_getNumArgTypes(type); for (int i = 0; i < numArgs; ++i) { func->addArgument(makeAbsType(clang_getArgType(type, i), parent)); } if (clang_isFunctionTypeVariadic(type)) { auto type = new DelayedType; static const auto id = IndexedTypeIdentifier(QStringLiteral("...")); type->setIdentifier(id); type->setKind(DelayedType::Unresolved); func->addArgument(AbstractType::Ptr(type)); } return func; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { DeclarationPointer decl = findDeclaration(clang_getTypeDeclaration(type)); DUChainReadLocker lock; if (!decl) { // probably a forward-declared type decl = ClangHelpers::findForwardDeclaration(type, m_parentContext->context, parent); } if (clang_Type_getNumTemplateArguments(type) != -1) { return createClassTemplateSpecializationType(type, decl); } auto t = new StructureType; if (decl) { t->setDeclaration(decl.data()); } else { // fallback, at least give the spelling to the user t->setDeclarationId(DeclarationId(IndexedQualifiedIdentifier(QualifiedIdentifier(ClangString(clang_getTypeSpelling(type)).toString())))); } return t; } template = dummy> AbstractType *createType(CXType type, CXCursor) { auto t = new EnumerationType; setIdTypeDecl(clang_getTypeDeclaration(type), t); return t; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto t = new TypeAliasType; CXCursor location = clang_getTypeDeclaration(type); t->setType(makeAbsType(clang_getTypedefDeclUnderlyingType(location), parent)); setIdTypeDecl(location, t); return t; } template = dummy> AbstractType *createType(CXType, CXCursor /*parent*/) { auto t = new DelayedType; static const IndexedTypeIdentifier id(QLatin1String(CursorKindTraits::delayedTypeName(TK))); t->setIdentifier(id); return t; } template = dummy> AbstractType *createType(CXType type, CXCursor /*parent*/) { return createDelayedType(type); } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto numTA = clang_Type_getNumTemplateArguments(type); // TODO: We should really expose more types to libclang! if (numTA != -1 && ClangString(clang_getTypeSpelling(type)).toString().contains(QLatin1Char('<'))) { return createClassTemplateSpecializationType(type); } // Maybe it's the ElaboratedType. E.g.: "struct Type foo();" or "NS::Type foo();" or "void foo(enum Enum e);" e.t.c. auto oldType = type; type = clang_getCanonicalType(type); bool isElaboratedType = type.kind != CXType_FunctionProto && type.kind != CXType_FunctionNoProto && type.kind != CXType_Unexposed && type.kind != CXType_Invalid && type.kind != CXType_Record; return !isElaboratedType ? createDelayedType(oldType) : makeType(type, parent); } template = dummy> typename IdType::Type *createType(CXCursor) { return new typename IdType::Type; } template = dummy> EnumeratorType *createType(CXCursor cursor) { auto type = new EnumeratorType; type->setValue(clang_getEnumConstantDeclUnsignedValue(cursor)); return type; } template = dummy> TypeAliasType *createType(CXCursor cursor) { auto type = new TypeAliasType; type->setType(makeAbsType(clang_getTypedefDeclUnderlyingType(cursor), cursor)); return type; } template = dummy> AbstractType* createType(CXCursor cursor) { auto clangType = clang_getCursorType(cursor); #if CINDEX_VERSION_MINOR < 31 if (clangType.kind == CXType_Unexposed) { // Clang sometimes can return CXType_Unexposed for CXType_FunctionProto kind. E.g. if it's AttributedType. return dispatchType(clangType, cursor); } #endif return makeType(clangType, cursor); } template = dummy> AbstractType *createType(CXCursor) { auto t = new DelayedType; static const IndexedTypeIdentifier id(QStringLiteral("Label")); t->setIdentifier(id); return t; } template = dummy> AbstractType *createType(CXCursor cursor) { auto clangType = clang_getCursorType(cursor); return makeType(clangType, cursor); } -#if CINDEX_VERSION_MINOR >= 31 +#if CINDEX_VERSION_MINOR >= 32 template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto deducedType = clang_getCanonicalType(type); bool isDeduced = deducedType.kind != CXType_Invalid && deducedType.kind != CXType_Auto; return !isDeduced ? createDelayedType(type) : makeType(deducedType, parent); } #endif #if CINDEX_VERSION_MINOR >= 34 template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto underyingType = clang_Type_getNamedType(type); return makeType(underyingType, parent); } #endif /// @param declaration an optional declaration that will be associated with created type AbstractType* createClassTemplateSpecializationType(CXType type, const DeclarationPointer declaration = {}) { auto numTA = clang_Type_getNumTemplateArguments(type); Q_ASSERT(numTA != -1); auto typeDecl = clang_getTypeDeclaration(type); if (!declaration && typeDecl.kind == CXCursor_NoDeclFound) { // clang_getTypeDeclaration doesn't handle all types, fall back to delayed type... return createDelayedType(type); } QStringList typesStr; QString tStr = ClangString(clang_getTypeSpelling(type)).toString(); ParamIterator iter(QStringLiteral("<>"), tStr); while (iter) { typesStr.append(*iter); ++iter; } auto cst = new ClassSpecializationType; for (int i = 0; i < numTA; i++) { auto argumentType = clang_Type_getTemplateArgumentAsType(type, i); AbstractType::Ptr currentType; if (argumentType.kind == CXType_Invalid) { if(i >= typesStr.size()){ currentType = createDelayedType(argumentType); } else { auto t = new DelayedType; t->setIdentifier(IndexedTypeIdentifier(typesStr[i])); currentType = t; } } else { currentType = makeType(argumentType, typeDecl); } if (currentType) { cst->addParameter(currentType->indexed()); } } auto decl = declaration ? declaration : findDeclaration(typeDecl); DUChainReadLocker lock; if (decl) { cst->setDeclaration(decl.data()); } else { // fallback, at least give the spelling to the user Identifier id(tStr); id.clearTemplateIdentifiers(); cst->setDeclarationId(DeclarationId(IndexedQualifiedIdentifier(QualifiedIdentifier(id)))); } return cst; } //END create* //BEGIN setDeclData template void setDeclData(CXCursor cursor, Declaration *decl, bool setComment = true) const; template void setDeclData(CXCursor cursor, MacroDefinition* decl) const; template void setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const; template = dummy> void setDeclData(CXCursor cursor, ClassDeclaration* decl) const; template = dummy> void setDeclData(CXCursor cursor, ClassDeclaration* decl) const; template void setDeclData(CXCursor cursor, AbstractFunctionDeclaration* decl) const; template void setDeclData(CXCursor cursor, ClassFunctionDeclaration* decl) const; template void setDeclData(CXCursor cursor, FunctionDeclaration *decl, bool setComment = true) const; template void setDeclData(CXCursor cursor, FunctionDefinition *decl) const; template void setDeclData(CXCursor cursor, NamespaceAliasDeclaration *decl) const; //END setDeclData //BEGIN setDeclInCtxtData template void setDeclInCtxtData(CXCursor, Declaration*) { //No-op } template void setDeclInCtxtData(CXCursor cursor, ClassFunctionDeclaration *decl) { // HACK to retrieve function-constness // This looks like a bug in Clang -- In theory setTypeModifiers should take care of setting the const modifier // however, clang_isConstQualifiedType() for TK == CXType_FunctionProto always returns false // TODO: Debug further auto type = decl->abstractType(); if (type) { if (clang_CXXMethod_isConst(cursor)) { type->setModifiers(type->modifiers() | AbstractType::ConstModifier); decl->setAbstractType(type); } } } template void setDeclInCtxtData(CXCursor cursor, FunctionDefinition *def) { setDeclInCtxtData(cursor, static_cast(def)); const CXCursor canon = clang_getCanonicalCursor(cursor); if (auto decl = findDeclaration(canon)) { def->setDeclaration(decl.data()); } } //END setDeclInCtxtData //BEGIN setDeclType template void setDeclType(Declaration *decl, typename IdType::Type *type) { setDeclType(decl, static_cast(type)); setDeclType(decl, static_cast(type)); } template void setDeclType(Declaration *decl, IdentifiedType *type) { type->setDeclaration(decl); } template void setDeclType(Declaration *decl, AbstractType *type) { decl->setAbstractType(AbstractType::Ptr(type)); } //END setDeclType template void setTypeModifiers(CXType type, AbstractType* kdevType) const; const CXFile m_file; const IncludeFileContexts &m_includes; DeclarationPointer findDeclaration(CXCursor cursor) const; void setIdTypeDecl(CXCursor typeCursor, IdentifiedType* idType) const; std::unordered_map> m_uses; /// At these location offsets (cf. @ref clang_getExpansionLocation) we encountered macro expansions QSet m_macroExpansionLocations; mutable QHash m_cursorToDeclarationCache; CurrentContext *m_parentContext; const bool m_update; }; //BEGIN setTypeModifiers template void Visitor::setTypeModifiers(CXType type, AbstractType* kdevType) const { quint64 modifiers = 0; if (clang_isConstQualifiedType(type)) { modifiers |= AbstractType::ConstModifier; } if (clang_isVolatileQualifiedType(type)) { modifiers |= AbstractType::VolatileModifier; } if (TK == CXType_Short || TK == CXType_UShort) { modifiers |= AbstractType::ShortModifier; } if (TK == CXType_Long || TK == CXType_LongDouble || TK == CXType_ULong) { modifiers |= AbstractType::LongModifier; } if (TK == CXType_LongLong || TK == CXType_ULongLong) { modifiers |= AbstractType::LongLongModifier; } if (TK == CXType_SChar) { modifiers |= AbstractType::SignedModifier; } if (TK == CXType_UChar || TK == CXType_UInt || TK == CXType_UShort || TK == CXType_UInt128 || TK == CXType_ULong || TK == CXType_ULongLong) { modifiers |= AbstractType::UnsignedModifier; } kdevType->setModifiers(modifiers); } //END setTypeModifiers //BEGIN dispatchCursor template> CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent) { const bool decision = CursorKindTraits::isClass(clang_getCursorKind(parent)); return decision ? dispatchCursor(cursor, parent) : dispatchCursor(cursor, parent); } template> CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent) { IF_DEBUG(clangDebug() << "IsInClass:" << IsInClass << "- isDefinition:" << IsDefinition;) const bool isDefinition = clang_isCursorDefinition(cursor); return isDefinition ? dispatchCursor(cursor, parent) : dispatchCursor(cursor, parent); } template> CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent) { IF_DEBUG(clangDebug() << "IsInClass:" << IsInClass << "- isDefinition:" << IsDefinition;) // We may end up visiting the same cursor twice in some cases // see discussion on https://git.reviewboard.kde.org/r/119526/ // TODO: Investigate why this is happening in libclang if ((CursorKindTraits::isClass(CK) || CK == CXCursor_EnumDecl) && clang_getCursorKind(parent) == CXCursor_VarDecl) { return CXChildVisit_Continue; } constexpr bool isClassMember = IsInClass == Decision::True; constexpr bool isDefinition = IsDefinition == Decision::True; // always build a context for class templates and functions, otherwise we "leak" // the function/template parameter declarations into the surrounding context, // which can lead to interesting bugs, like https://bugs.kde.org/show_bug.cgi?id=368067 constexpr bool hasContext = isDefinition || CursorKindTraits::isFunction(CK) || CursorKindTraits::isClassTemplate(CK); return buildDeclaration::Type, hasContext>(cursor); } //END dispatchCursor //BEGIN setDeclData template void Visitor::setDeclData(CXCursor cursor, Declaration *decl, bool setComment) const { if (setComment) decl->setComment(makeComment(clang_Cursor_getParsedComment(cursor))); if (CursorKindTraits::isAliasType(CK)) { decl->setIsTypeAlias(true); } if (CK == CXCursor_Namespace) decl->setKind(Declaration::Namespace); if (CK == CXCursor_EnumDecl || CK == CXCursor_EnumConstantDecl || CursorKindTraits::isClass(CK) || CursorKindTraits::isAliasType(CK)) decl->setKind(Declaration::Type); int isAlwaysDeprecated; clang_getCursorPlatformAvailability(cursor, &isAlwaysDeprecated, nullptr, nullptr, nullptr, nullptr, 0); decl->setDeprecated(isAlwaysDeprecated); } template void Visitor::setDeclData(CXCursor cursor, MacroDefinition* decl) const { setDeclData(cursor, static_cast(decl)); if (m_update) { decl->clearParameters(); } auto unit = clang_Cursor_getTranslationUnit(cursor); auto range = clang_getCursorExtent(cursor); // TODO: Quite lacking API in libclang here. // No way to find out if this macro is function-like or not // cf. http://clang.llvm.org/doxygen/classclang_1_1MacroInfo.html // And no way to get the actual definition text range // Should be quite easy to expose that in libclang, though // Let' still get some basic support for this and parse on our own, it's not that difficult const QString contents = QString::fromUtf8(ClangUtils::getRawContents(unit, range)); const int firstOpeningParen = contents.indexOf(QLatin1Char('(')); const int firstWhitespace = contents.indexOf(QLatin1Char(' ')); const bool isFunctionLike = (firstOpeningParen != -1) && (firstOpeningParen < firstWhitespace); decl->setFunctionLike(isFunctionLike); // now extract the actual definition text int start = -1; if (isFunctionLike) { const int closingParen = findClose(contents, firstOpeningParen); if (closingParen != -1) { start = closingParen + 2; // + ')' + ' ' // extract macro function parameters const QString parameters = contents.mid(firstOpeningParen, closingParen - firstOpeningParen + 1); ParamIterator paramIt(QStringLiteral("():"), parameters, 0); while (paramIt) { decl->addParameter(IndexedString(*paramIt)); ++paramIt; } } } else { start = firstWhitespace + 1; // + ' ' } if (start == -1) { // unlikely: invalid macro definition, insert the complete #define statement decl->setDefinition(IndexedString(QLatin1String("#define ") + contents)); } else if (start < contents.size()) { decl->setDefinition(IndexedString(contents.mid(start))); } // else: macro has no body => leave the definition text empty } template void Visitor::setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const { setDeclData(cursor, static_cast(decl)); //A CXCursor_VarDecl in a class is static (otherwise it'd be a CXCursor_FieldDecl) if (CK == CXCursor_VarDecl) decl->setStatic(true); decl->setAccessPolicy(CursorKindTraits::kdevAccessPolicy(clang_getCXXAccessSpecifier(cursor))); -#if CINDEX_VERSION_MINOR >= 31 +#if CINDEX_VERSION_MINOR >= 32 decl->setMutable(clang_CXXField_isMutable(cursor)); #endif #if CINDEX_VERSION_MINOR >= 30 if (!jsonTestRun()) { auto offset = clang_Cursor_getOffsetOfField(cursor); if (offset >= 0) { // don't add this info to the json tests, it invalidates the comment structure auto type = clang_getCursorType(cursor); auto sizeOf = clang_Type_getSizeOf(type); auto alignedTo = clang_Type_getAlignOf(type); const auto byteOffset = offset / 8; const auto bitOffset = offset % 8; const QString byteOffsetStr = i18np("1 Byte", "%1 Bytes", byteOffset); const QString bitOffsetStr = bitOffset ? i18np("1 Bit", "%1 Bits", bitOffset) : QString(); const QString offsetStr = bitOffset ? i18nc("%1: bytes, %2: bits", "%1, %2", byteOffsetStr, bitOffsetStr) : byteOffsetStr; decl->setComment(decl->comment() + i18n("

offset in parent: %1; " "size: %2 Bytes; " "aligned to: %3 Bytes

", offsetStr, sizeOf, alignedTo).toUtf8()); } } #endif } template> void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const { CXCursorKind kind = clang_getTemplateCursorKind(cursor); switch (kind) { case CXCursor_UnionDecl: setDeclData(cursor, decl); break; case CXCursor_StructDecl: setDeclData(cursor, decl); break; case CXCursor_ClassDecl: setDeclData(cursor, decl); break; default: Q_ASSERT(false); break; } } template> void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const { if (m_update) { decl->clearBaseClasses(); } setDeclData(cursor, static_cast(decl)); if (CK == CXCursor_UnionDecl) decl->setClassType(ClassDeclarationData::Union); if (CK == CXCursor_StructDecl) decl->setClassType(ClassDeclarationData::Struct); if (clang_isCursorDefinition(cursor)) { decl->setDeclarationIsDefinition(true); } if (!jsonTestRun()) { // don't add this info to the json tests, it invalidates the comment structure auto type = clang_getCursorType(cursor); auto sizeOf = clang_Type_getSizeOf(type); auto alignOf = clang_Type_getAlignOf(type); if (sizeOf >= 0 && alignOf >= 0) { decl->setComment(decl->comment() + i18n("

size: %1 Bytes; " "aligned to: %2 Bytes

", sizeOf, alignOf).toUtf8()); } } } template void Visitor::setDeclData(CXCursor cursor, AbstractFunctionDeclaration* decl) const { if (m_update) { decl->clearDefaultParameters(); } // No setDeclData(...) here: AbstractFunctionDeclaration is an interface // TODO: Can we get the default arguments directly from Clang? // also see http://clang-developers.42468.n3.nabble.com/Finding-default-value-for-function-argument-with-clang-c-API-td4036919.html const QVector defaultArgs = ClangUtils::getDefaultArguments(cursor, ClangUtils::MinimumSize); foreach (const QString& defaultArg, defaultArgs) { decl->addDefaultParameter(IndexedString(defaultArg)); } } template void Visitor::setDeclData(CXCursor cursor, ClassFunctionDeclaration* decl) const { setDeclData(cursor, static_cast(decl)); setDeclData(cursor, static_cast(decl)); decl->setAbstract(clang_CXXMethod_isPureVirtual(cursor)); decl->setStatic(clang_CXXMethod_isStatic(cursor)); decl->setVirtual(clang_CXXMethod_isVirtual(cursor)); const auto qtAttribute = ClangUtils::specialQtAttributes(cursor); decl->setIsSignal(qtAttribute == ClangUtils::QtSignalAttribute); decl->setIsSlot(qtAttribute == ClangUtils::QtSlotAttribute); } template void Visitor::setDeclData(CXCursor cursor, FunctionDeclaration *decl, bool setComment) const { setDeclData(cursor, static_cast(decl)); setDeclData(cursor, static_cast(decl), setComment); } template void Visitor::setDeclData(CXCursor cursor, FunctionDefinition *decl) const { bool setComment = clang_equalCursors(clang_getCanonicalCursor(cursor), cursor); setDeclData(cursor, static_cast(decl), setComment); } template void Visitor::setDeclData(CXCursor cursor, NamespaceAliasDeclaration *decl) const { setDeclData(cursor, static_cast(decl)); clang_visitChildren(cursor, [] (CXCursor cursor, CXCursor parent, CXClientData data) -> CXChildVisitResult { if (clang_getCursorKind(cursor) == CXCursor_NamespaceRef) { const auto id = QualifiedIdentifier(ClangString(clang_getCursorSpelling(cursor)).toString()); reinterpret_cast(data)->setImportIdentifier(id); return CXChildVisit_Break; } else { return visitCursor(cursor, parent, data); } }, decl); } //END setDeclData //BEGIN build* template CXChildVisitResult Visitor::buildDeclaration(CXCursor cursor) { auto id = makeId(cursor); if (CK == CXCursor_UnexposedDecl && id.isEmpty()) { // skip unexposed declarations that have no identifier set // this is useful to skip e.g. friend declarations return CXChildVisit_Recurse; } IF_DEBUG(clangDebug() << "id:" << id << "- CK:" << CK << "- DeclType:" << typeid(DeclType).name() << "- hasContext:" << hasContext;) // Code path for class declarations that may be defined "out-of-line", e.g. // "SomeNameSpace::SomeClass {};" QScopedPointer helperContext; if (CursorKindTraits::isClass(CK) || CursorKindTraits::isFunction(CK)) { const auto lexicalParent = clang_getCursorLexicalParent(cursor); const auto semanticParent = clang_getCursorSemanticParent(cursor); const bool isOutOfLine = !clang_equalCursors(lexicalParent, semanticParent); if (isOutOfLine) { const QString scope = ClangUtils::getScope(cursor); auto context = createContext(cursor, QualifiedIdentifier(scope)); helperContext.reset(new CurrentContext(context, m_parentContext->keepAliveContexts)); } } // if helperContext is null, this is a no-op PushValue pushCurrent(m_parentContext, helperContext.isNull() ? m_parentContext : helperContext.data()); if (hasContext) { auto context = createContext(cursor, QualifiedIdentifier(id)); createDeclaration(cursor, id, context); CurrentContext newParent(context, m_parentContext->keepAliveContexts); PushValue pushCurrent(m_parentContext, &newParent); clang_visitChildren(cursor, &visitCursor, this); return CXChildVisit_Continue; } createDeclaration(cursor, id, nullptr); return CXChildVisit_Recurse; } CXChildVisitResult Visitor::buildParmDecl(CXCursor cursor) { return buildDeclaration::Type, false>(cursor); } CXChildVisitResult Visitor::buildUse(CXCursor cursor) { m_uses[m_parentContext->context].push_back(cursor); return cursor.kind == CXCursor_DeclRefExpr || cursor.kind == CXCursor_MemberRefExpr ? CXChildVisit_Recurse : CXChildVisit_Continue; } CXChildVisitResult Visitor::buildMacroExpansion(CXCursor cursor) { buildUse(cursor); // cache that we encountered a macro expansion at this location unsigned int offset; clang_getSpellingLocation(clang_getCursorLocation(cursor), nullptr, nullptr, nullptr, &offset); m_macroExpansionLocations << offset; return CXChildVisit_Recurse; } CXChildVisitResult Visitor::buildCompoundStatement(CXCursor cursor) { if (m_parentContext->context->type() == DUContext::Function) { auto context = createContext(cursor); CurrentContext newParent(context, m_parentContext->keepAliveContexts); PushValue pushCurrent(m_parentContext, &newParent); clang_visitChildren(cursor, &visitCursor, this); return CXChildVisit_Continue; } return CXChildVisit_Recurse; } CXChildVisitResult Visitor::buildCXXBaseSpecifier(CXCursor cursor) { auto currentContext = m_parentContext->context; bool virtualInherited = clang_isVirtualBase(cursor); Declaration::AccessPolicy access = CursorKindTraits::kdevAccessPolicy(clang_getCXXAccessSpecifier(cursor)); auto classDeclCursor = clang_getCursorReferenced(cursor); auto decl = findDeclaration(classDeclCursor); if (!decl) { // this happens for templates with template-dependent base classes e.g. - dunno whether we can/should do more here clangDebug() << "failed to find declaration for base specifier:" << clang_getCursorDisplayName(cursor); return CXChildVisit_Recurse; } DUChainWriteLocker lock; contextImportDecl(currentContext, decl); auto classDecl = dynamic_cast(currentContext->owner()); Q_ASSERT(classDecl); classDecl->addBaseClass({decl->indexedType(), access, virtualInherited}); return CXChildVisit_Recurse; } //END build* DeclarationPointer Visitor::findDeclaration(CXCursor cursor) const { const auto it = m_cursorToDeclarationCache.constFind(cursor); if (it != m_cursorToDeclarationCache.constEnd()) { return *it; } // fallback, and cache result auto decl = ClangHelpers::findDeclaration(cursor, m_includes); m_cursorToDeclarationCache.insert(cursor, decl); return decl; } void Visitor::setIdTypeDecl(CXCursor typeCursor, IdentifiedType* idType) const { DeclarationPointer decl = findDeclaration(typeCursor); DUChainReadLocker lock; if (decl) { idType->setDeclaration(decl.data()); } } AbstractType *Visitor::makeType(CXType type, CXCursor parent) { #define UseKind(TypeKind) case TypeKind: return dispatchType(type, parent) switch (type.kind) { UseKind(CXType_Void); UseKind(CXType_Bool); UseKind(CXType_Short); UseKind(CXType_UShort); UseKind(CXType_Int); UseKind(CXType_UInt); UseKind(CXType_Long); UseKind(CXType_ULong); UseKind(CXType_LongLong); UseKind(CXType_ULongLong); UseKind(CXType_Float); UseKind(CXType_LongDouble); UseKind(CXType_Double); UseKind(CXType_Char_U); UseKind(CXType_Char_S); UseKind(CXType_UChar); UseKind(CXType_SChar); UseKind(CXType_Char16); UseKind(CXType_Char32); UseKind(CXType_Pointer); UseKind(CXType_BlockPointer); UseKind(CXType_MemberPointer); UseKind(CXType_ObjCObjectPointer); UseKind(CXType_ConstantArray); UseKind(CXType_VariableArray); UseKind(CXType_IncompleteArray); UseKind(CXType_DependentSizedArray); UseKind(CXType_LValueReference); UseKind(CXType_RValueReference); UseKind(CXType_FunctionNoProto); UseKind(CXType_FunctionProto); UseKind(CXType_Record); UseKind(CXType_Enum); UseKind(CXType_Typedef); UseKind(CXType_Int128); UseKind(CXType_UInt128); UseKind(CXType_Vector); UseKind(CXType_Unexposed); UseKind(CXType_WChar); UseKind(CXType_ObjCInterface); UseKind(CXType_ObjCId); UseKind(CXType_ObjCClass); UseKind(CXType_ObjCSel); UseKind(CXType_NullPtr); -#if CINDEX_VERSION_MINOR >= 31 +#if CINDEX_VERSION_MINOR >= 32 UseKind(CXType_Auto); #endif #if CINDEX_VERSION_MINOR >= 34 UseKind(CXType_Elaborated); #endif case CXType_Invalid: return nullptr; default: qCWarning(KDEV_CLANG) << "Unhandled type:" << type.kind << clang_getTypeSpelling(type); return nullptr; } #undef UseKind } RangeInRevision rangeInRevisionForUse(CXCursor cursor, CXCursorKind referencedCursorKind, CXSourceRange useRange, const QSet& macroExpansionLocations) { auto range = ClangRange(useRange).toRangeInRevision(); //TODO: Fix in clang, happens for operator<<, operator<, probably more if (clang_Range_isNull(useRange)) { useRange = clang_getCursorExtent(cursor); range = ClangRange(useRange).toRangeInRevision(); } if (referencedCursorKind == CXCursor_ConversionFunction) { range.end = range.start; range.start.column--; } // For uses inside macro expansions, create an empty use range at the spelling location // the empty range is required in order to not "overlap" the macro expansion range // and to allow proper navigation for the macro expansion // also see JSON test 'macros.cpp' if (clang_getCursorKind(cursor) != CXCursor_MacroExpansion) { unsigned int expansionLocOffset; const auto spellingLocation = clang_getRangeStart(useRange); clang_getExpansionLocation(spellingLocation, nullptr, nullptr, nullptr, &expansionLocOffset); if (macroExpansionLocations.contains(expansionLocOffset)) { unsigned int spellingLocOffset; clang_getSpellingLocation(spellingLocation, nullptr, nullptr, nullptr, &spellingLocOffset); if (spellingLocOffset == expansionLocOffset) { range.end = range.start; } } } else { // Workaround for wrong use range returned by clang for macro expansions const auto contents = ClangUtils::getRawContents(clang_Cursor_getTranslationUnit(cursor), useRange); const int firstOpeningParen = contents.indexOf('('); if (firstOpeningParen != -1) { range.end.column = range.start.column + firstOpeningParen; range.end.line = range.start.line; } } return range; } Visitor::Visitor(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update) : m_file(file) , m_includes(includes) , m_parentContext(nullptr) , m_update(update) { CXCursor tuCursor = clang_getTranslationUnitCursor(tu); auto top = includes[file]; // when updating, this contains child contexts that should be kept alive // even when they are not part of the AST anymore // this is required for some assistants, such as the signature assistant QSet keepAliveContexts; { DUChainReadLocker lock; foreach (const auto& problem, top->problems()) { const auto& desc = problem->description(); if (desc.startsWith(QLatin1String("Return type of out-of-line definition of '")) && desc.endsWith(QLatin1String("' differs from that in the declaration"))) { auto ctx = top->findContextAt(problem->range().start); // keep the context and its parents alive // this also keeps declarations in this context alive while (ctx) { keepAliveContexts << ctx; ctx = ctx->parentContext(); } } } } CurrentContext parent(top, keepAliveContexts); m_parentContext = &parent; clang_visitChildren(tuCursor, &visitCursor, this); if (m_update) { DUChainWriteLocker lock; top->deleteUsesRecursively(); } for (const auto &contextUses : m_uses) { for (const auto &cursor : contextUses.second) { auto referenced = referencedCursor(cursor); if (clang_Cursor_isNull(referenced)) { continue; } // first, try the canonical referenced cursor // this is important to get the correct function declaration e.g. auto canonicalReferenced = clang_getCanonicalCursor(referenced); auto used = findDeclaration(canonicalReferenced); if (!used) { // if the above failed, try the non-canonicalized version as a fallback // this is required for friend declarations that occur before // the real declaration. there, the canonical cursor points to // the friend declaration which is not what we are looking for used = findDeclaration(referenced); } if (!used) { // as a last resort, try to resolve the forward declaration DUChainReadLocker lock; DeclarationPointer decl = ClangHelpers::findForwardDeclaration(clang_getCursorType(referenced), contextUses.first, referenced); used = decl; if (!used) { continue; } } #if CINDEX_VERSION_MINOR >= 29 if (clang_Cursor_getNumTemplateArguments(referenced) >= 0) { // Ideally, we don't need this, but for function templates clang_getCanonicalCursor returns a function definition // See also the testUsesCreatedForDeclarations test DUChainReadLocker lock; used = DUChainUtils::declarationForDefinition(used.data()); } #endif const auto useRange = clang_getCursorReferenceNameRange(cursor, 0, 0); const auto range = rangeInRevisionForUse(cursor, referenced.kind, useRange, m_macroExpansionLocations); DUChainWriteLocker lock; auto usedIndex = top->indexForUsedDeclaration(used.data()); contextUses.first->createUse(usedIndex, range); } } } //END Visitor CXChildVisitResult visitCursor(CXCursor cursor, CXCursor parent, CXClientData data) { Visitor *visitor = static_cast(data); const auto kind = clang_getCursorKind(cursor); auto location = clang_getCursorLocation(cursor); CXFile file; clang_getFileLocation(location, &file, nullptr, nullptr, nullptr); // don't skip MemberRefExpr with invalid location, see also: // http://lists.cs.uiuc.edu/pipermail/cfe-dev/2015-May/043114.html if (!ClangUtils::isFileEqual(file, visitor->m_file) && (file || kind != CXCursor_MemberRefExpr)) { return CXChildVisit_Continue; } #define UseCursorKind(CursorKind, ...) case CursorKind: return visitor->dispatchCursor(__VA_ARGS__); switch (kind) { UseCursorKind(CXCursor_UnexposedDecl, cursor, parent); UseCursorKind(CXCursor_StructDecl, cursor, parent); UseCursorKind(CXCursor_UnionDecl, cursor, parent); UseCursorKind(CXCursor_ClassDecl, cursor, parent); UseCursorKind(CXCursor_EnumDecl, cursor, parent); UseCursorKind(CXCursor_FieldDecl, cursor, parent); UseCursorKind(CXCursor_EnumConstantDecl, cursor, parent); UseCursorKind(CXCursor_FunctionDecl, cursor, parent); UseCursorKind(CXCursor_VarDecl, cursor, parent); UseCursorKind(CXCursor_TypeAliasDecl, cursor, parent); UseCursorKind(CXCursor_TypedefDecl, cursor, parent); UseCursorKind(CXCursor_CXXMethod, cursor, parent); UseCursorKind(CXCursor_Namespace, cursor, parent); UseCursorKind(CXCursor_NamespaceAlias, cursor, parent); UseCursorKind(CXCursor_Constructor, cursor, parent); UseCursorKind(CXCursor_Destructor, cursor, parent); UseCursorKind(CXCursor_ConversionFunction, cursor, parent); UseCursorKind(CXCursor_TemplateTypeParameter, cursor, parent); UseCursorKind(CXCursor_NonTypeTemplateParameter, cursor, parent); UseCursorKind(CXCursor_TemplateTemplateParameter, cursor, parent); UseCursorKind(CXCursor_FunctionTemplate, cursor, parent); UseCursorKind(CXCursor_ClassTemplate, cursor, parent); UseCursorKind(CXCursor_ClassTemplatePartialSpecialization, cursor, parent); UseCursorKind(CXCursor_ObjCInterfaceDecl, cursor, parent); UseCursorKind(CXCursor_ObjCCategoryDecl, cursor, parent); UseCursorKind(CXCursor_ObjCProtocolDecl, cursor, parent); UseCursorKind(CXCursor_ObjCPropertyDecl, cursor, parent); UseCursorKind(CXCursor_ObjCIvarDecl, cursor, parent); UseCursorKind(CXCursor_ObjCInstanceMethodDecl, cursor, parent); UseCursorKind(CXCursor_ObjCClassMethodDecl, cursor, parent); UseCursorKind(CXCursor_ObjCImplementationDecl, cursor, parent); UseCursorKind(CXCursor_ObjCCategoryImplDecl, cursor, parent); UseCursorKind(CXCursor_MacroDefinition, cursor, parent); UseCursorKind(CXCursor_LabelStmt, cursor, parent); case CXCursor_TypeRef: case CXCursor_TemplateRef: case CXCursor_NamespaceRef: case CXCursor_MemberRef: case CXCursor_LabelRef: case CXCursor_OverloadedDeclRef: case CXCursor_VariableRef: case CXCursor_DeclRefExpr: case CXCursor_MemberRefExpr: case CXCursor_ObjCClassRef: return visitor->buildUse(cursor); case CXCursor_MacroExpansion: return visitor->buildMacroExpansion(cursor); case CXCursor_CompoundStmt: return visitor->buildCompoundStatement(cursor); case CXCursor_CXXBaseSpecifier: return visitor->buildCXXBaseSpecifier(cursor); case CXCursor_ParmDecl: return visitor->buildParmDecl(cursor); default: return CXChildVisit_Recurse; } } } namespace Builder { void visit(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update) { Visitor visitor(tu, file, includes, update); } } diff --git a/projectmanagers/cmake/CMakeLists.txt b/projectmanagers/cmake/CMakeLists.txt index 81868d5d79..5097f358ec 100644 --- a/projectmanagers/cmake/CMakeLists.txt +++ b/projectmanagers/cmake/CMakeLists.txt @@ -1,116 +1,116 @@ project(cmakemanager) add_definitions(-DTRANSLATION_DOMAIN=\"kdevcmake\") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/parser) add_subdirectory(tests) add_subdirectory(icons) # enable this if you want to have the cmake debug visitor run on each CMakeLists.txt # the debug visitor prints out the Ast for the CMakeLists.txt file. #add_definitions( -DCMAKEDEBUGVISITOR ) add_definitions( -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) set( cmakecommon_SRCS parser/cmListFileLexer.c # parser/astfactory.cpp # parser/cmakelistsparser.cpp # parser/cmakeast.cpp # parser/cmakecondition.cpp # parser/cmakeprojectvisitor.cpp # parser/variablemap.cpp # parser/cmakedebugvisitor.cpp parser/cmakecachereader.cpp parser/cmakelistsparser.cpp parser/cmakeduchaintypes.cpp # parser/cmakeparserutils.cpp # parser/cmakeduchaintypes.cpp # parser/generationexpressionsolver.cpp cmakeutils.cpp cmakebuilddirchooser.cpp debug.cpp ) set( cmakecommon_UI cmakebuilddirchooser.ui ) set( cmakemanager_SRCS testing/ctestutils.cpp testing/ctestfindjob.cpp testing/ctestrunjob.cpp testing/ctestsuite.cpp testing/qttestdelegate.cpp cmakeimportjsonjob.cpp cmakenavigationwidget.cpp cmakemanager.cpp cmakemodelitems.cpp duchain/cmakeparsejob.cpp duchain/usebuilder.cpp duchain/declarationbuilder.cpp duchain/contextbuilder.cpp cmakecodecompletionmodel.cpp # cmakecommitchangesjob.cpp # cmakeimportjob.cpp # cmakeedit.cpp debug.cpp ) set( cmakemanager_UI cmakepossibleroots.ui ) set( cmakesettings_SRCS settings/cmakepreferences.cpp settings/cmakecachemodel.cpp settings/cmakecachedelegate.cpp settings/cmakecachemodel.cpp debug.cpp ) ki18n_wrap_ui(cmakesettings_SRCS settings/cmakebuildsettings.ui) set( cmakedoc_SRCS cmakedocumentation.cpp cmakehelpdocumentation.cpp ) remove_definitions( -DQT_NO_STL ) if(WIN32) add_definitions(-DYY_NO_UNISTD_H) endif() # Note: This library doesn't follow API/ABI/BC rules and shouldn't have a SOVERSION # Its only purpose is to support the plugin without needing to add all source files # to the plugin target ki18n_wrap_ui( cmakecommon_SRCS ${cmakecommon_UI} ) add_library( kdevcmakecommon SHARED ${cmakecommon_SRCS} ) target_link_libraries( kdevcmakecommon KF5::TextEditor KDev::Interfaces KDev::Project KDev::Util KDev::Language ) generate_export_header(kdevcmakecommon EXPORT_FILE_NAME cmakecommonexport.h) ki18n_wrap_ui( cmakemanager_SRCS ${cmakemanager_UI} ) add_library( kdevcmakemanagernosettings STATIC ${cmakemanager_SRCS}) target_link_libraries( kdevcmakemanagernosettings KF5::KIOWidgets KDev::Util KDev::Interfaces kdevcmakecommon kdevmakefileresolver KDev::Project KDev::Language KDev::OutputView KF5::TextEditor Qt5::Concurrent) kdevplatform_add_plugin(kdevcmakemanager JSON kdevcmakemanager.json SOURCES ${cmakemanager_SRCS} ${cmakesettings_SRCS}) target_link_libraries( kdevcmakemanager KF5::KIOWidgets KDev::Util KDev::Interfaces kdevcmakecommon kdevmakefileresolver KDev::Project KDev::Language KDev::OutputView KF5::TextEditor Qt5::Concurrent) kdevplatform_add_plugin(kdevcmakedocumentation JSON kdevcmakedocumentation.json SOURCES ${cmakedoc_SRCS}) target_link_libraries( kdevcmakedocumentation KDev::Interfaces kdevcmakecommon KDev::Project KDev::Language KDev::Documentation - KF5::TextEditor Qt5::WebKitWidgets) + KF5::TextEditor) install(TARGETS kdevcmakecommon ${INSTALL_TARGETS_DEFAULT_ARGS} )