diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp index 5a635cadc4..a10fa21002 100644 --- a/src/plugin/plugin.cpp +++ b/src/plugin/plugin.cpp @@ -1,293 +1,293 @@ /* * This file is part of KDevelop * * Copyright 2016 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 "plugin.h" #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 "job.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()); + pms->addModel(QStringLiteral("Clangtidy"), i18n("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(CLANG_TIDY_PATH); } 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 == nullptr) { 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 == nullptr) { 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()) { auto job = new ClangTidy::Job(params, this); core()->runController()->registerJob(job); params.dumpConfig = QString(); } auto 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 == nullptr) { return; } if (aj->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded) { m_model->setProblems(aj->problems()); core()->uiController()->findToolView(i18nd("kdevproblemreporter", "Problems"), nullptr, 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("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; } 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; } return new ClangtidyPreferences(this, parent); } } // namespace ClangTidy #include "plugin.moc"