File indexing completed on 2024-05-05 16:45:14
0001 /* 0002 SPDX-FileCopyrightText: 2013 Christoph Thielecke <crissi99@gmx.de> 0003 SPDX-FileCopyrightText: 2016-2017 Anton Anikin <anton.anikin@htower.ru> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "plugin.h" 0009 0010 #include "config/globalconfigpage.h" 0011 #include "config/projectconfigpage.h" 0012 #include "globalsettings.h" 0013 0014 #include "debug.h" 0015 #include "job.h" 0016 #include "problemmodel.h" 0017 0018 #include <interfaces/contextmenuextension.h> 0019 #include <interfaces/icore.h> 0020 #include <interfaces/idocumentcontroller.h> 0021 #include <interfaces/iprojectcontroller.h> 0022 #include <interfaces/iruncontroller.h> 0023 #include <interfaces/iuicontroller.h> 0024 #include <language/interfaces/editorcontext.h> 0025 #include <project/projectconfigpage.h> 0026 #include <project/projectmodel.h> 0027 #include <util/jobstatus.h> 0028 0029 #include <KActionCollection> 0030 #include <KPluginFactory> 0031 0032 #include <QAction> 0033 #include <QMimeDatabase> 0034 0035 K_PLUGIN_FACTORY_WITH_JSON(CppcheckFactory, "kdevcppcheck.json", registerPlugin<cppcheck::Plugin>();) 0036 0037 namespace cppcheck 0038 { 0039 0040 Plugin::Plugin(QObject* parent, const QVariantList&) 0041 : IPlugin(QStringLiteral("kdevcppcheck"), parent) 0042 , m_job(nullptr) 0043 , m_currentProject(nullptr) 0044 , m_model(new ProblemModel(this)) 0045 { 0046 qCDebug(KDEV_CPPCHECK) << "setting cppcheck rc file"; 0047 setXMLFile(QStringLiteral("kdevcppcheck.rc")); 0048 0049 QIcon cppcheckIcon = QIcon::fromTheme(QStringLiteral("cppcheck")); 0050 0051 m_menuActionFile = new QAction(cppcheckIcon, i18nc("@action", "Analyze Current File with Cppcheck"), this); 0052 connect(m_menuActionFile, &QAction::triggered, this, [this](){ 0053 runCppcheck(false); 0054 }); 0055 actionCollection()->addAction(QStringLiteral("cppcheck_file"), m_menuActionFile); 0056 0057 m_contextActionFile = new QAction(cppcheckIcon, i18nc("@item:inmenu", "Cppcheck"), this); 0058 connect(m_contextActionFile, &QAction::triggered, this, [this]() { 0059 runCppcheck(false); 0060 }); 0061 0062 m_menuActionProject = new QAction(cppcheckIcon, i18nc("@action", "Analyze Current Project with Cppcheck"), this); 0063 connect(m_menuActionProject, &QAction::triggered, this, [this](){ 0064 runCppcheck(true); 0065 }); 0066 actionCollection()->addAction(QStringLiteral("cppcheck_project"), m_menuActionProject); 0067 0068 m_contextActionProject = new QAction(cppcheckIcon, i18nc("@item:inmenu", "Cppcheck"), this); 0069 connect(m_contextActionProject, &QAction::triggered, this, [this]() { 0070 runCppcheck(true); 0071 }); 0072 0073 m_contextActionProjectItem = new QAction(cppcheckIcon, i18nc("@item:inmenu", "Cppcheck"), this); 0074 0075 connect(core()->documentController(), &KDevelop::IDocumentController::documentClosed, 0076 this, &Plugin::updateActions); 0077 connect(core()->documentController(), &KDevelop::IDocumentController::documentActivated, 0078 this, &Plugin::updateActions); 0079 0080 connect(core()->projectController(), &KDevelop::IProjectController::projectOpened, 0081 this, &Plugin::updateActions); 0082 connect(core()->projectController(), &KDevelop::IProjectController::projectClosed, 0083 this, &Plugin::projectClosed); 0084 0085 updateActions(); 0086 } 0087 0088 Plugin::~Plugin() 0089 { 0090 killCppcheck(); 0091 } 0092 0093 bool Plugin::isRunning() 0094 { 0095 return m_job; 0096 } 0097 0098 void Plugin::killCppcheck() 0099 { 0100 if (m_job) { 0101 m_job->kill(); 0102 } 0103 } 0104 0105 void Plugin::raiseProblemsView() 0106 { 0107 m_model->show(); 0108 } 0109 0110 void Plugin::raiseOutputView() 0111 { 0112 core()->uiController()->findToolView( 0113 i18nc("@title:window", "Test"), 0114 nullptr, 0115 KDevelop::IUiController::FindFlags::Raise); 0116 } 0117 0118 void Plugin::updateActions() 0119 { 0120 m_currentProject = nullptr; 0121 0122 m_menuActionFile->setEnabled(false); 0123 m_menuActionProject->setEnabled(false); 0124 0125 if (isRunning()) { 0126 return; 0127 } 0128 0129 KDevelop::IDocument* activeDocument = core()->documentController()->activeDocument(); 0130 if (!activeDocument) { 0131 return; 0132 } 0133 0134 QUrl url = activeDocument->url(); 0135 0136 m_currentProject = core()->projectController()->findProjectForUrl(url); 0137 if (!m_currentProject) { 0138 return; 0139 } 0140 0141 m_menuActionFile->setEnabled(true); 0142 m_menuActionProject->setEnabled(true); 0143 } 0144 0145 void Plugin::projectClosed(KDevelop::IProject* project) 0146 { 0147 if (project != m_model->project()) { 0148 return; 0149 } 0150 0151 killCppcheck(); 0152 m_model->reset(); 0153 } 0154 0155 void Plugin::runCppcheck(bool checkProject) 0156 { 0157 KDevelop::IDocument* doc = core()->documentController()->activeDocument(); 0158 Q_ASSERT(doc); 0159 0160 if (checkProject) { 0161 runCppcheck(m_currentProject, m_currentProject->path().toUrl().toLocalFile()); 0162 } else { 0163 runCppcheck(m_currentProject, doc->url().toLocalFile()); 0164 } 0165 } 0166 0167 void Plugin::runCppcheck(KDevelop::IProject* project, const QString& path) 0168 { 0169 m_model->reset(project, path); 0170 0171 Parameters params(project); 0172 params.checkPath = path; 0173 0174 m_job = new Job(params); 0175 0176 connect(m_job, &Job::problemsDetected, m_model.data(), &ProblemModel::addProblems); 0177 connect(m_job, &Job::finished, this, &Plugin::result); 0178 0179 core()->uiController()->registerStatus(new KDevelop::JobStatus(m_job, QStringLiteral("Cppcheck"))); 0180 core()->runController()->registerJob(m_job); 0181 0182 if (params.hideOutputView) { 0183 raiseProblemsView(); 0184 } else { 0185 raiseOutputView(); 0186 } 0187 0188 updateActions(); 0189 } 0190 0191 void Plugin::result(KJob*) 0192 { 0193 if (!core()->projectController()->projects().contains(m_model->project())) { 0194 m_model->reset(); 0195 } else { 0196 m_model->setProblems(); 0197 0198 if (m_job->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded || 0199 m_job->status() == KDevelop::OutputExecuteJob::JobStatus::JobCanceled) { 0200 raiseProblemsView(); 0201 } else { 0202 raiseOutputView(); 0203 } 0204 } 0205 0206 m_job = nullptr; // job is automatically deleted later 0207 0208 updateActions(); 0209 } 0210 0211 static 0212 bool isSupportedMimeType(const QMimeType& mimeType) 0213 { 0214 const QString mimeName = mimeType.name(); 0215 return (mimeName == QLatin1String("text/x-c++src") || 0216 mimeName == QLatin1String("text/x-c++hdr") || 0217 mimeName == QLatin1String("text/x-chdr") || 0218 mimeName == QLatin1String("text/x-csrc")); 0219 } 0220 0221 KDevelop::ContextMenuExtension Plugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) 0222 { 0223 KDevelop::ContextMenuExtension extension = KDevelop::IPlugin::contextMenuExtension(context, parent); 0224 0225 if (context->hasType(KDevelop::Context::EditorContext) && m_currentProject && !isRunning()) { 0226 auto eContext = static_cast<KDevelop::EditorContext*>(context); 0227 QMimeDatabase db; 0228 const auto mime = db.mimeTypeForUrl(eContext->url()); 0229 0230 if (isSupportedMimeType(mime)) { 0231 extension.addAction(KDevelop::ContextMenuExtension::AnalyzeFileGroup, m_contextActionFile); 0232 extension.addAction(KDevelop::ContextMenuExtension::AnalyzeProjectGroup, m_contextActionProject); 0233 } 0234 } 0235 0236 if (context->hasType(KDevelop::Context::ProjectItemContext) && !isRunning()) { 0237 auto pContext = static_cast<KDevelop::ProjectItemContext*>(context); 0238 if (pContext->items().size() != 1) { 0239 return extension; 0240 } 0241 0242 auto item = pContext->items().first(); 0243 0244 switch (item->type()) { 0245 case KDevelop::ProjectBaseItem::File: { 0246 const QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(item->path().toUrl()); 0247 if (!isSupportedMimeType(mimetype)) { 0248 return extension; 0249 } 0250 break; 0251 } 0252 case KDevelop::ProjectBaseItem::Folder: 0253 case KDevelop::ProjectBaseItem::BuildFolder: 0254 break; 0255 0256 default: 0257 return extension; 0258 } 0259 0260 m_contextActionProjectItem->disconnect(); 0261 connect(m_contextActionProjectItem, &QAction::triggered, this, [this, item](){ 0262 runCppcheck(item->project(), item->path().toLocalFile()); 0263 }); 0264 0265 extension.addAction(KDevelop::ContextMenuExtension::AnalyzeProjectGroup, m_contextActionProjectItem); 0266 } 0267 0268 return extension; 0269 } 0270 0271 KDevelop::ConfigPage* Plugin::perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent) 0272 { 0273 return number ? nullptr : new ProjectConfigPage(this, options.project, parent); 0274 } 0275 0276 KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent) 0277 { 0278 return number ? nullptr : new GlobalConfigPage(this, parent); 0279 } 0280 0281 } 0282 0283 #include "plugin.moc" 0284 #include "moc_plugin.cpp"