File indexing completed on 2024-04-21 12:11:06

0001 /* This file is part of KDevelop
0002 
0003    Copyright 2016 Anton Anikin <anton.anikin@htower.ru>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This program is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013    General Public License for more details.
0014 
0015    You should have received a copy of the GNU General Public License
0016    along with this program; see the file COPYING.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "plugin.h"
0022 
0023 #include "config/globalconfigpage.h"
0024 #include "config/projectconfigpage.h"
0025 #include "debug.h"
0026 #include "problemmodel.h"
0027 #include "rules.h"
0028 #include "utils.h"
0029 
0030 #include <interfaces/contextmenuextension.h>
0031 #include <interfaces/icore.h>
0032 #include <interfaces/idocumentcontroller.h>
0033 #include <interfaces/iprojectcontroller.h>
0034 #include <interfaces/iruncontroller.h>
0035 #include <interfaces/iuicontroller.h>
0036 #include <kactioncollection.h>
0037 #include <kmessagebox.h>
0038 #include <kpluginfactory.h>
0039 #include <language/interfaces/editorcontext.h>
0040 #include <project/projectconfigpage.h>
0041 #include <project/projectmodel.h>
0042 #include <util/jobstatus.h>
0043 
0044 #include <QApplication>
0045 #include <QFile>
0046 
0047 K_PLUGIN_FACTORY_WITH_JSON(VerappFactory, "kdevverapp.json", registerPlugin<verapp::Plugin>();)
0048 
0049 namespace verapp
0050 {
0051 
0052 Plugin::Plugin(QObject* parent, const QVariantList&)
0053     : IPlugin("kdevverapp", parent)
0054     , m_job(nullptr)
0055     , m_project(nullptr)
0056     , m_model(new ProblemModel(this))
0057 {
0058     qCDebug(KDEV_VERAPP) << "setting kdevverapp.rc file";
0059     setXMLFile("kdevverapp.rc");
0060 
0061     rules::init();
0062 
0063     m_menuActionFile = new QAction(i18n("Analyze Current File with Vera++"), this);
0064     connect(m_menuActionFile, &QAction::triggered, [this](){
0065         runVerapp(false);
0066     });
0067     actionCollection()->addAction("verapp_file", m_menuActionFile);
0068 
0069     m_contextActionFile = new QAction(i18n("Vera++"), this);
0070     connect(m_contextActionFile, &QAction::triggered, [this]() {
0071         runVerapp(false);
0072     });
0073 
0074     m_menuActionProject = new QAction(i18n("Analyze Current Project with Vera++"), this);
0075     connect(m_menuActionProject, &QAction::triggered, [this](){
0076         runVerapp(true);
0077     });
0078     actionCollection()->addAction("verapp_project", m_menuActionProject);
0079 
0080     m_contextActionProject = new QAction(i18n("Vera++"), this);
0081     connect(m_contextActionProject, &QAction::triggered, [this]() {
0082         runVerapp(true);
0083     });
0084 
0085     m_contextActionProjectItem = new QAction("Vera++", this);
0086 
0087     connect(core()->documentController(), &KDevelop::IDocumentController::documentClosed,
0088             this, &Plugin::updateActions);
0089     connect(core()->documentController(), &KDevelop::IDocumentController::documentActivated,
0090             this, &Plugin::updateActions);
0091 
0092     connect(core()->projectController(), &KDevelop::IProjectController::projectOpened,
0093             this, &Plugin::updateActions);
0094     connect(core()->projectController(), &KDevelop::IProjectController::projectClosed,
0095             this, &Plugin::projectClosed);
0096 
0097     updateActions();
0098 }
0099 
0100 Plugin::~Plugin()
0101 {
0102     killVerapp();
0103 }
0104 
0105 bool Plugin::isRunning()
0106 {
0107     return m_job;
0108 }
0109 
0110 void Plugin::killVerapp()
0111 {
0112     if (m_job) {
0113         m_job->kill(KJob::EmitResult);
0114     }
0115 }
0116 
0117 void Plugin::raiseProblemsView()
0118 {
0119     m_model->show();
0120 }
0121 
0122 void Plugin::raiseOutputView()
0123 {
0124     core()->uiController()->findToolView(
0125         i18ndc("kdevstandardoutputview", "@title:window", "Test"),
0126         nullptr,
0127         KDevelop::IUiController::FindFlags::Raise);
0128 }
0129 
0130 void Plugin::updateActions()
0131 {
0132     m_project = nullptr;
0133 
0134     m_menuActionFile->setEnabled(false);
0135     m_menuActionProject->setEnabled(false);
0136 
0137     if (isRunning()) {
0138         return;
0139     }
0140 
0141     auto activeDocument = core()->documentController()->activeDocument();
0142     if (!activeDocument) {
0143         return;
0144     }
0145 
0146     m_project = core()->projectController()->findProjectForUrl(activeDocument->url());
0147     if (!m_project) {
0148         return;
0149     }
0150 
0151     m_menuActionFile->setEnabled(true);
0152     m_menuActionProject->setEnabled(true);
0153 }
0154 
0155 void Plugin::projectClosed(KDevelop::IProject* project)
0156 {
0157     if (project != m_model->project()) {
0158         return;
0159     }
0160 
0161     killVerapp();
0162     m_model->reset();
0163 }
0164 
0165 void Plugin::runVerapp(bool checkProject)
0166 {
0167     auto doc = core()->documentController()->activeDocument();
0168     Q_ASSERT(doc);
0169 
0170     if (checkProject) {
0171         runVerapp(m_project, m_project->path().toUrl().toLocalFile());
0172     } else {
0173         runVerapp(m_project, doc->url().toLocalFile());
0174     }
0175 }
0176 
0177 void Plugin::runVerapp(KDevelop::IProject* project, const QString& path)
0178 {
0179     Parameters params(project);
0180     params.checkPath = path;
0181 
0182     m_model->reset(project, path);
0183 
0184     if (!QFile::exists(params.executablePath)) {
0185         QString errorMessage;
0186         errorMessage += i18n("Failed to start vera++ from \"%1\".", params.executablePath);
0187         errorMessage += QStringLiteral("\n\n");
0188         errorMessage += i18n("Check your settings and install the vera++ if necessary.");
0189 
0190         KMessageBox::error(qApp->activeWindow(), errorMessage, i18n("Vera++ Error"));
0191         return;
0192     }
0193 
0194     m_job = new Job(params);
0195 
0196     connect(m_job, &Job::problemsDetected, m_model.data(), &ProblemModel::addProblems);
0197     connect(m_job, &Job::finished, this, &Plugin::result);
0198 
0199     core()->uiController()->registerStatus(new KDevelop::JobStatus(m_job, "vera++"));
0200     core()->runController()->registerJob(m_job);
0201 
0202     if (params.hideOutputView) {
0203         raiseProblemsView();
0204     } else {
0205         raiseOutputView();
0206     }
0207 
0208     updateActions();
0209 }
0210 
0211 void Plugin::result(KJob*)
0212 {
0213     if (!core()->projectController()->projects().contains(m_model->project())) {
0214         m_model->reset();
0215     } else {
0216         m_model->setProblems();
0217 
0218         if (m_job->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded ||
0219             m_job->status() == KDevelop::OutputExecuteJob::JobStatus::JobCanceled) {
0220 
0221             raiseProblemsView();
0222         } else {
0223             raiseOutputView();
0224         }
0225     }
0226 
0227     m_job = nullptr; // job is automatically deleted later
0228 
0229     updateActions();
0230 }
0231 
0232 KDevelop::ContextMenuExtension Plugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent)
0233 {
0234     Q_UNUSED(parent);
0235     KDevelop::ContextMenuExtension extension;
0236 
0237     if (context->hasType(KDevelop::Context::EditorContext) && m_project && !isRunning()) {
0238         auto eContext = static_cast<KDevelop::EditorContext*>(context);
0239         if (isSupportedUrl(eContext->url()))
0240         {
0241             extension.addAction(KDevelop::ContextMenuExtension::AnalyzeFileGroup, m_contextActionFile);
0242         }
0243 
0244         extension.addAction(KDevelop::ContextMenuExtension::AnalyzeProjectGroup, m_contextActionProject);
0245     }
0246 
0247     if (context->hasType(KDevelop::Context::ProjectItemContext) && !isRunning()) {
0248         auto pContext = dynamic_cast<KDevelop::ProjectItemContext*>(context);
0249         if (pContext->items().size() != 1) {
0250             return extension;
0251         }
0252 
0253         auto item = pContext->items().first();
0254 
0255         switch (item->type()) {
0256             case KDevelop::ProjectBaseItem::File: {
0257                 if (!isSupportedUrl(item->path().toUrl())) {
0258                     return extension;
0259                 }
0260                 break;
0261             }
0262             case KDevelop::ProjectBaseItem::Folder:
0263             case KDevelop::ProjectBaseItem::BuildFolder:
0264                 break;
0265 
0266             default:
0267                 return extension;
0268         }
0269 
0270         m_contextActionProjectItem->disconnect();
0271         connect(m_contextActionProjectItem, &QAction::triggered, [this, item](){
0272             runVerapp(item->project(), item->path().toLocalFile());
0273         });
0274 
0275         extension.addAction(KDevelop::ContextMenuExtension::AnalyzeProjectGroup, m_contextActionProjectItem);
0276     }
0277 
0278     return extension;
0279 }
0280 
0281 KDevelop::ConfigPage* Plugin::perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent)
0282 {
0283     if (number) {
0284         return nullptr;
0285     }
0286 
0287     return new ProjectConfigPage(this, options.project, parent);
0288 }
0289 
0290 KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent)
0291 {
0292     if (number) {
0293         return nullptr;
0294     }
0295 
0296     return new GlobalConfigPage(this, parent);
0297 }
0298 
0299 }
0300 
0301 #include "plugin.moc"