diff --git a/src/config/clangtidypreferences.cpp b/src/config/clangtidypreferences.cpp index 47a4d0a8b7..4f9ed5f031 100644 --- a/src/config/clangtidypreferences.cpp +++ b/src/config/clangtidypreferences.cpp @@ -1,65 +1,70 @@ /************************************************************************************* * Copyright (C) 2016 by Carlos Nihelton * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU 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 "clangtidypreferences.h" #include #include "clangtidyconfig.h" #include "configgroup.h" #include "ui_clangtidysettings.h" using KDevelop::IPlugin; using ClangTidy::ConfigGroup; +using KDevelop::ConfigPage; ClangtidyPreferences::ClangtidyPreferences(IPlugin* plugin, QWidget* parent) : ConfigPage(plugin, ClangtidySettings::self(), parent) { QVBoxLayout* layout = new QVBoxLayout(this); QWidget* widget = new QWidget(this); ui = new Ui::ClangtidySettings(); ui->setupUi(widget); layout->addWidget(widget); } ClangtidyPreferences::~ClangtidyPreferences() { delete ui; } +ConfigPage::ConfigPageType ClangtidyPreferences::configPageType() const{ + return ConfigPage::AnalyzerConfigPage; +} + QString ClangtidyPreferences::name() const { return i18n("clang-tidy"); } QString ClangtidyPreferences::fullName() const { return i18n("Configure clang-tidy settings"); } QIcon ClangtidyPreferences::icon() const { - return QIcon::fromTheme(QStringLiteral("kdevelop")); + return QIcon::fromTheme(QStringLiteral("dialog-ok")); } void ClangtidyPreferences::apply() { ConfigGroup projConf = KSharedConfig::openConfig()->group("Clangtidy"); projConf.writeEntry(ConfigGroup::ExecutablePath, ui->kcfg_clangtidyPath->text()); } diff --git a/src/config/clangtidypreferences.h b/src/config/clangtidypreferences.h index 814f087157..587f9cd805 100644 --- a/src/config/clangtidypreferences.h +++ b/src/config/clangtidypreferences.h @@ -1,49 +1,50 @@ /************************************************************************************* * Copyright (C) 2016 by Carlos Nihelton * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU 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 CLANGTIDYPREFERENCES_H #define CLANGTIDYPREFERENCES_H #include namespace Ui { class ClangtidySettings; }; /** * \class * \brief Implements the session configuration page for clang-tidy. */ class ClangtidyPreferences : public KDevelop::ConfigPage { Q_OBJECT public: explicit ClangtidyPreferences(KDevelop::IPlugin* plugin = nullptr, QWidget* parent = nullptr); ~ClangtidyPreferences() override; + ConfigPage::ConfigPageType configPageType() const override; QString name() const override; QString fullName() const override; QIcon icon() const override; public slots: void apply() override; private: Ui::ClangtidySettings* ui; }; #endif diff --git a/src/config/perprojectconfigpage.cpp b/src/config/perprojectconfigpage.cpp index 8fd4f47032..447c04f6cb 100644 --- a/src/config/perprojectconfigpage.cpp +++ b/src/config/perprojectconfigpage.cpp @@ -1,166 +1,175 @@ /************************************************************************************* * Copyright (C) 2016 by Carlos Nihelton * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU 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 "perprojectconfigpage.h" #include "debug.h" #include "plugin.h" #include "ui_genericconfig.h" #include #include namespace ClangTidy { PerProjectConfigPage::PerProjectConfigPage(KDevelop::IProject* project, QWidget* parent) : ConfigPage(nullptr, nullptr, parent) , m_project(project) { ui = new Ui::GenericConfig(); ui->setupUi(this); m_availableChecksModel = new QStringListModel(); ui->checkListView->setModel(m_availableChecksModel); m_selectedItemModel = new QItemSelectionModel(m_availableChecksModel); ui->checkListView->setSelectionModel(m_selectedItemModel); m_config = m_project->projectConfiguration()->group("Clangtidy"); } PerProjectConfigPage::~PerProjectConfigPage(void) { delete ui; } +QIcon PerProjectConfigPage::icon() const +{ + return QIcon::fromTheme(QStringLiteral("dialog-ok")); +} + +KDevelop::ConfigPage::ConfigPageType PerProjectConfigPage::configPageType() const{ + return ConfigPage::AnalyzerConfigPage; +} + QString PerProjectConfigPage::name() const { return i18n("clang-tidy"); } void PerProjectConfigPage::setActiveChecksReceptorList(QStringList* list) { m_activeChecksReceptor = list; *m_activeChecksReceptor = m_config.readEntry(ConfigGroup::EnabledChecks).split(','); } void PerProjectConfigPage::setList(QStringList list) { m_underlineAvailChecks = list; m_availableChecksModel->setStringList(m_underlineAvailChecks); for (int i = 0; i < m_availableChecksModel->rowCount(); ++i) { QModelIndex index = m_availableChecksModel->index(i, 0); if (index.isValid()) { if (m_activeChecksReceptor->contains((index.data().toString()))) { m_selectedItemModel->select(index, QItemSelectionModel::Select); } else { m_selectedItemModel->select(index, QItemSelectionModel::Deselect); } } } } void PerProjectConfigPage::apply() { // TODO: discover a way to set the project folders where user header files might exist into this option. // Right now it only works with manual entry. m_config.writeEntry(ConfigGroup::HeaderFilter, ui->headerFilterText->text()); m_config.writeEntry(ConfigGroup::AdditionalParameters, ui->clangtidyParameters->text()); m_config.enableEntry(ConfigGroup::CheckSystemHeaders, ui->sysHeadersCheckBox->isChecked()); m_config.enableEntry(ConfigGroup::UseConfigFile, !ui->overrideConfigFileCheckBox->isChecked()); m_config.enableEntry(ConfigGroup::DumpConfig, ui->dumpCheckBox->isChecked()); for (int i = 0; i < m_availableChecksModel->rowCount(); ++i) { QModelIndex index = m_availableChecksModel->index(i, 0); if (index.isValid()) { bool isSelected = m_selectedItemModel->isSelected(index); auto check = index.data().toString(); if (isSelected) { *m_activeChecksReceptor << check; } else { m_activeChecksReceptor->removeAll(check); } } } m_activeChecksReceptor->removeDuplicates(); m_config.writeEntry(ConfigGroup::EnabledChecks, m_activeChecksReceptor->join(',')); } void PerProjectConfigPage::defaults() { bool wasBlocked = signalsBlocked(); blockSignals(true); // TODO: discover a way to set the project folders where user header files might exist into this option. // Right now it only works with manual entry. m_config.writeEntry(ConfigGroup::ExecutablePath, "/usr/bin/clang-tidy"); m_config.writeEntry(ConfigGroup::HeaderFilter, ""); ui->headerFilterText->setText(""); m_config.writeEntry(ConfigGroup::AdditionalParameters, ""); ui->clangtidyParameters->setText(QString("")); m_config.writeEntry(ConfigGroup::CheckSystemHeaders, ""); ui->sysHeadersCheckBox->setChecked(false); m_config.enableEntry(ConfigGroup::UseConfigFile, false); ui->overrideConfigFileCheckBox->setChecked(true); ui->CheckListGroupBox->setEnabled(true); m_config.enableEntry(ConfigGroup::DumpConfig, true); ui->dumpCheckBox->setChecked(true); ui->CheckListGroupBox->setEnabled(true); m_config.enableEntry(ConfigGroup::ExportFixes, true); // ui->autoFixCheckBox->setChecked(true); for (int i = 0; i < m_availableChecksModel->rowCount(); ++i) { QModelIndex index = m_availableChecksModel->index(i, 0); if (index.isValid()) { auto check = index.data().toString(); bool enable = check.contains("cert") || check.contains("-core.") || check.contains("-cplusplus") || check.contains("-deadcode") || check.contains("-security") || check.contains("cppcoreguide"); m_selectedItemModel->select(index, enable ? QItemSelectionModel::Select : QItemSelectionModel::Deselect); if (enable) { *m_activeChecksReceptor << check; } else { m_activeChecksReceptor->removeAll(check); } } } m_activeChecksReceptor->removeDuplicates(); m_config.writeEntry(ConfigGroup::EnabledChecks, m_activeChecksReceptor->join(',')); blockSignals(wasBlocked); } void PerProjectConfigPage::reset() { if (!m_config.isValid()) { return; } ui->headerFilterText->setText(m_config.readEntry(ConfigGroup::HeaderFilter).remove("--header-filter=")); ui->clangtidyParameters->setText(m_config.readEntry(ConfigGroup::AdditionalParameters)); ui->sysHeadersCheckBox->setChecked(!m_config.readEntry(ConfigGroup::CheckSystemHeaders).isEmpty()); ui->overrideConfigFileCheckBox->setChecked(m_config.readEntry(ConfigGroup::UseConfigFile).isEmpty()); ui->CheckListGroupBox->setEnabled(m_config.readEntry(ConfigGroup::UseConfigFile).isEmpty()); ui->dumpCheckBox->setChecked(!m_config.readEntry(ConfigGroup::DumpConfig).isEmpty()); } } // namespace ClangTidy diff --git a/src/config/perprojectconfigpage.h b/src/config/perprojectconfigpage.h index 4926b3f91a..b4b411f16c 100644 --- a/src/config/perprojectconfigpage.h +++ b/src/config/perprojectconfigpage.h @@ -1,78 +1,80 @@ /************************************************************************************* * Copyright (C) 2016 by Carlos Nihelton * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU 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 CLANGTIDY_PERPROJECTCONFIGPAGE_H_ #define CLANGTIDY_PERPROJECTCONFIGPAGE_H_ #include "configgroup.h" #include #include #include #include class QIcon; class QStringListModel; namespace KDevelop { class IProject; } namespace ClangTidy { class Plugin; namespace Ui { class GenericConfig; } /** * \class * \brief Implements the clang-tidy's configuration project for the current project. */ class PerProjectConfigPage : public KDevelop::ConfigPage { Q_OBJECT public: PerProjectConfigPage(KDevelop::IProject* project, QWidget* parent); ~PerProjectConfigPage() override; + ConfigPageType configPageType() const override; QString name() const override; + QIcon icon() const override; void setList(QStringList list); void setActiveChecksReceptorList(QStringList* list); signals: public slots: void apply() override; void defaults() override; void reset() override; private: KDevelop::IProject* m_project; Ui::GenericConfig* ui; ConfigGroup m_config; QStringList* m_activeChecksReceptor; QStringList m_underlineAvailChecks; QStringListModel* m_availableChecksModel; QItemSelectionModel* m_selectedItemModel; }; } #endif /* CLANGTIDY_PERPROJECTCONFIGPAGE_H_ */ diff --git a/src/plugin.cpp b/src/plugin.cpp index 10d58b4544..5031607a47 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,290 +1,290 @@ /************************************************************************************* * Copyright (C) 2016 by Carlos Nihelton * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "./config/clangtidypreferences.h" #include "./config/perprojectconfigpage.h" #include "debug.h" #include "job.h" #include "plugin.h" using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(ClangtidyFactory, "res/kdevclangtidy.json", registerPlugin();) namespace ClangTidy { Plugin::Plugin(QObject* parent, const QVariantList& /*unused*/) : IPlugin("kdevclangtidy", parent) , m_model(new KDevelop::ProblemModel(parent)) { qCDebug(KDEV_CLANGTIDY) << "setting clangtidy rc file"; setXMLFile("kdevclangtidy.rc"); QAction* act_checkfile; act_checkfile = actionCollection()->addAction("clangtidy_file", this, SLOT(runClangtidyFile())); act_checkfile->setStatusTip(i18n("Launches Clangtidy for current file")); act_checkfile->setText(i18n("clang-tidy")); /* TODO: Uncomment this only when discover a safe way to run clang-tidy on the whole project. // QAction* act_check_all_files; // act_check_all_files = actionCollection()->addAction ( "clangtidy_all", this, SLOT ( runClangtidyAll() ) ); // act_check_all_files->setStatusTip ( i18n ( "Launches clangtidy for all translation " // "units of current project" ) ); // act_check_all_files->setText ( i18n ( "clang-tidy (all)" ) ); */ IExecutePlugin* iface = KDevelop::ICore::self() ->pluginController() ->pluginForExtension("org.kdevelop.IExecutePlugin") ->extension(); Q_ASSERT(iface); ProblemModelSet* pms = core()->languageController()->problemModelSet(); pms->addModel(QStringLiteral("Clangtidy"), m_model.data()); m_config = KSharedConfig::openConfig()->group("Clangtidy"); auto clangtidyPath = m_config.readEntry(ConfigGroup::ExecutablePath); // TODO(cnihelton): auto detect clang-tidy executable instead of hard-coding it. if (clangtidyPath.isEmpty()) { clangtidyPath = QString("/usr/bin/clang-tidy"); } collectAllAvailableChecks(clangtidyPath); m_config.writeEntry(ConfigGroup::AdditionalParameters, ""); for (auto check : m_allChecks) { bool enable = check.contains("cert") || check.contains("-core.") || check.contains("-cplusplus") || check.contains("-deadcode") || check.contains("-security") || check.contains("cppcoreguide"); if (enable) { m_activeChecks << check; } else { m_activeChecks.removeAll(check); } } m_activeChecks.removeDuplicates(); m_config.writeEntry(ConfigGroup::EnabledChecks, m_activeChecks.join(',')); } void Plugin::unload() { ProblemModelSet* pms = core()->languageController()->problemModelSet(); pms->removeModel(QStringLiteral("Clangtidy")); } void Plugin::collectAllAvailableChecks(QString clangtidyPath) { m_allChecks.clear(); KProcess tidy; tidy << clangtidyPath << QLatin1String("-checks=*") << QLatin1String("--list-checks"); tidy.setOutputChannelMode(KProcess::OnlyStdoutChannel); tidy.start(); if (!tidy.waitForStarted()) { qCDebug(KDEV_CLANGTIDY) << "Unable to execute clang-tidy."; return; } tidy.closeWriteChannel(); if (!tidy.waitForFinished()) { qCDebug(KDEV_CLANGTIDY) << "Failed during clang-tidy execution."; return; } QTextStream ios(&tidy); QString each; while (ios.readLineInto(&each)) { m_allChecks.append(each.trimmed()); } if (m_allChecks.size() > 3) { m_allChecks.removeAt(m_allChecks.length() - 1); m_allChecks.removeAt(0); } m_allChecks.removeDuplicates(); } void Plugin::runClangtidy(bool allFiles) { KDevelop::IDocument* doc = core()->documentController()->activeDocument(); if (!doc) { QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"), i18n("No suitable active file, unable to deduce project.")); return; } KDevelop::IProject* project = core()->projectController()->findProjectForUrl(doc->url()); if (!project) { QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"), i18n("Active file isn't in a project")); return; } m_config = project->projectConfiguration()->group("Clangtidy"); if (!m_config.isValid()) { QMessageBox::critical(nullptr, i18n("Error starting Clangtidy"), i18n("Can't load parameters. They must be set in the project settings.")); return; } auto clangtidyPath = m_config.readEntry(ConfigGroup::ExecutablePath); auto buildSystem = project->buildSystemManager(); Job::Parameters params; params.projectRootDir = project->path().toLocalFile(); // TODO: auto detect clang-tidy executable instead of hard-coding it. if (clangtidyPath.isEmpty()) { params.executablePath = QStringLiteral("/usr/bin/clang-tidy"); } else { params.executablePath = clangtidyPath; } if (allFiles) { params.filePath = project->path().toUrl().toLocalFile(); } else { params.filePath = doc->url().toLocalFile(); } params.buildDir = buildSystem->buildDirectory(project->projectItem()).toLocalFile(); params.additionalParameters = m_config.readEntry(ConfigGroup::AdditionalParameters); params.analiseTempDtors = m_config.readEntry(ConfigGroup::AnaliseTempDtors); params.enabledChecks = m_activeChecks.join(','); params.useConfigFile = m_config.readEntry(ConfigGroup::UseConfigFile); params.dumpConfig = m_config.readEntry(ConfigGroup::DumpConfig); params.enableChecksProfile = m_config.readEntry(ConfigGroup::EnableChecksProfile); params.exportFixes = m_config.readEntry(ConfigGroup::ExportFixes); params.extraArgs = m_config.readEntry(ConfigGroup::ExtraArgs); params.extraArgsBefore = m_config.readEntry(ConfigGroup::ExtraArgsBefore); params.autoFix = m_config.readEntry(ConfigGroup::AutoFix); params.headerFilter = m_config.readEntry(ConfigGroup::HeaderFilter); params.lineFilter = m_config.readEntry(ConfigGroup::LineFilter); params.listChecks = m_config.readEntry(ConfigGroup::ListChecks); params.checkSystemHeaders = m_config.readEntry(ConfigGroup::CheckSystemHeaders); if (!params.dumpConfig.isEmpty()) { Job* job = new ClangTidy::Job(params, this); core()->runController()->registerJob(job); params.dumpConfig = QString(); } Job* job2 = new ClangTidy::Job(params, this); connect(job2, SIGNAL(finished(KJob*)), this, SLOT(result(KJob*))); core()->runController()->registerJob(job2); } void Plugin::runClangtidyFile() { bool allFiles = false; runClangtidy(allFiles); } void Plugin::runClangtidyAll() { bool allFiles = true; runClangtidy(allFiles); } void Plugin::loadOutput() { } void Plugin::result(KJob* job) { Job* aj = dynamic_cast(job); if (!aj) { return; } if (aj->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded) { m_model->setProblems(aj->problems()); core()->uiController()->findToolView(i18nd("kdevproblemreporter", "Problems"), 0, KDevelop::IUiController::FindFlags::Raise); } } KDevelop::ContextMenuExtension Plugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::IDocument* doc = core()->documentController()->activeDocument(); KDevelop::ContextMenuExtension extension = KDevelop::IPlugin::contextMenuExtension(context); if (context->type() == KDevelop::Context::EditorContext) { auto mime = doc->mimeType().name(); if (mime == QLatin1String("text/x-c++src") || mime == QLatin1String("text/x-csrc")) { QAction* action - = new QAction(QIcon::fromTheme("document-new"), i18n("Check current unit with clang-tidy"), this); + = new QAction(QIcon::fromTheme("dialog-ok"), i18n("Check unit with clang-tidy"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(runClangtidyFile())); extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, action); } } return extension; } KDevelop::ConfigPage* Plugin::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent) { if (number != 0) { return nullptr; } else { auto config = new PerProjectConfigPage(options.project, parent); config->setActiveChecksReceptorList(&m_activeChecks); config->setList(m_allChecks); return config; } } KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent) { if (number != 0) { return nullptr; } else { return new ClangtidyPreferences(this, parent); } } } #include "plugin.moc"