File indexing completed on 2024-04-21 04:34:33

0001 /*
0002  * This file is part of KDevelop Krazy2 Plugin.
0003  *
0004  * Copyright 2012 Daniel Calviño Sánchez <danxuliu@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License
0008  * as published by the Free Software Foundation; either version 2
0009  * of the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program. If not, see <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "krazy2plugin.h"
0021 
0022 #include <KAboutData>
0023 #include <KLocalizedString>
0024 #include <KPluginFactory>
0025 #include <KActionCollection>
0026 
0027 #include <interfaces/icore.h>
0028 #include <interfaces/iuicontroller.h>
0029 #include <interfaces/ilanguagecontroller.h>
0030 
0031 #include <interfaces/iruncontroller.h>
0032 #include <interfaces/idocumentcontroller.h>
0033 #include <interfaces/idocument.h>
0034 #include <interfaces/iprojectcontroller.h>
0035 #include <interfaces/iproject.h>
0036 
0037 #include <language/editor/documentrange.h>
0038 
0039 #include "./settings/krazy2preferences.h"
0040 #include "./settings/krazy2projectsettings.h"
0041 
0042 #include <QAction>
0043 
0044 #include <KLocalizedString>
0045 
0046 #include <kdevplatform/project/projectconfigpage.h>
0047 #include <shell/problemmodelset.h>
0048 #include <shell/problemmodel.h>
0049 #include <shell/problem.h>
0050 
0051 #include "./checker.h"
0052 #include "./checkerlistjob.h"
0053 #include "./analysisparameters.h"
0054 #include "./analysisjob.h"
0055 #include "./analysisresults.h"
0056 #include "./issue.h"
0057 
0058 #include <QMessageBox>
0059 
0060 //KPluginFactory stuff to load the plugin dynamically at runtime
0061 K_PLUGIN_FACTORY_WITH_JSON(KDevKrazy2Factory, "kdevkrazy2.json", registerPlugin<Krazy2Plugin>();)
0062 
0063 //public:
0064 
0065 Krazy2Plugin::Krazy2Plugin(QObject* parent, const QVariantList& /*= QVariantList()*/):
0066         KDevelop::IPlugin("kdevkrazy2", parent),
0067         m_analysisInProgress(false),
0068         m_model(new KDevelop::ProblemModel(parent)),
0069         m_results(nullptr){
0070     setXMLFile("kdevkrazy2.rc");
0071 
0072     grabCheckerList();
0073 
0074     auto checkFileAction = new QAction(this);
0075     checkFileAction->setText(i18n("Krazy2 check (selected files)"));
0076     checkFileAction->setStatusTip(i18n("Check selected files with Krazy2 tools"));
0077     connect(checkFileAction, &QAction::triggered, this, &Krazy2Plugin::checkSelectedFiles);
0078 
0079     auto checkAllFilesAction = new QAction(this);
0080     checkAllFilesAction->setText(i18n("Krazy2 check (all files)"));
0081     checkAllFilesAction->setStatusTip(i18n("Check all files with Krazy2 tools"));
0082     connect(checkAllFilesAction, &QAction::triggered, this, &Krazy2Plugin::checkAllFiles);
0083 
0084     actionCollection()->addAction("krazy2_selected", checkFileAction);
0085     actionCollection()->addAction("krazy2_all", checkAllFilesAction);
0086 
0087     KDevelop::ProblemModelSet *set = KDevelop::ICore::self()->languageController()->problemModelSet();
0088     set->addModel(QStringLiteral("Krazy2"), i18n("Krazy2"), m_model);
0089 }
0090 
0091 Krazy2Plugin::~Krazy2Plugin() {
0092 }
0093 
0094 void Krazy2Plugin::unload() {
0095     KDevelop::ProblemModelSet *set = KDevelop::ICore::self()->languageController()->problemModelSet();
0096     set->removeModel(QStringLiteral("Krazy2"));
0097 }
0098 
0099 KDevelop::ConfigPage* Krazy2Plugin::configPage(int number, QWidget *parent)
0100 {
0101     if (number != 0)
0102         return nullptr;
0103 
0104     return new Krazy2Preferences(this, parent);
0105 }
0106 
0107 KDevelop::ConfigPage* Krazy2Plugin::perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions &options, QWidget *parent)
0108 {
0109     if (number != 0)
0110         return nullptr;
0111 
0112     return new Krazy2ProjectSettings(options.project, &m_checkers, parent);
0113 }
0114 
0115 void Krazy2Plugin::checkSelectedFiles()
0116 {
0117     check(false);
0118 }
0119 
0120 void Krazy2Plugin::checkAllFiles()
0121 {
0122     check(true);
0123 }
0124 
0125 void Krazy2Plugin::onListingFinished(KJob *job)
0126 {
0127     if (job->error() != KJob::NoError) {
0128         QMessageBox::critical(nullptr,
0129                               i18n("Error gathering checker list"),
0130                               i18n("There was an error while gathering the checker list"));
0131         return;
0132     }
0133 }
0134 
0135 void Krazy2Plugin::onAnalysisFinished(KJob *job)
0136 {
0137     m_analysisInProgress = false;
0138 
0139     if (job->error() != KJob::NoError) {
0140         QMessageBox::critical(nullptr,
0141                               i18n("Error running Krazy2"),
0142                               i18n("There was an error with the Krazy2 process"));
0143         return;
0144     }
0145 
0146     // Retrieve list of found issues
0147     const QList<const Issue*> issues = m_results->issues();
0148     QVector<KDevelop::IProblem::Ptr> problems;
0149 
0150     // Build a Problem objects from these issues
0151     foreach (const Issue* issue, issues) {
0152         KDevelop::DocumentRange range;
0153         range.document = KDevelop::IndexedString(issue->fileName());
0154         range.setBothLines(issue->line());
0155 
0156         KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem());
0157         if(!issue->message().isEmpty())
0158             problem->setDescription(issue->message());
0159         else
0160             problem->setDescription(issue->checker()->description());
0161 
0162         if(problem->description().isEmpty())
0163             problem->setDescription(i18n("The tool didn't provide a message."));
0164 
0165         problem->setFinalLocation(range);
0166         problem->setSource(KDevelop::IProblem::Plugin);
0167         problem->setSeverity(KDevelop::IProblem::Warning);
0168 
0169         problems.push_back(problem);
0170     }
0171 
0172     m_model->setProblems(problems);
0173 }
0174 
0175 void Krazy2Plugin::grabCheckerList()
0176 {
0177     auto job = new CheckerListJob(this);
0178     job->setCheckerList(&m_checkers);
0179 
0180     connect(job, &KJob::finished, this, &Krazy2Plugin::onListingFinished);
0181 
0182     core()->runController()->registerJob(job);
0183 }
0184 
0185 void Krazy2Plugin::check(bool allFiles)
0186 {
0187     if (m_analysisInProgress) {
0188         QMessageBox::critical(nullptr,
0189                               i18n("Error starting Krazy2"),
0190                               i18n("Analysis already in progress"));
0191         return;
0192     }
0193 
0194     if (m_checkers.isEmpty()) {
0195         QMessageBox::critical(nullptr,
0196                               i18n("Error starting Krazy2"),
0197                               i18n("Gathering the checkers list hasn't finished yet or has failed."));
0198         return;
0199     }
0200 
0201     KDevelop::IDocument *doc = KDevelop::ICore::self()->documentController()->activeDocument();
0202     if (!doc) {
0203         QMessageBox::critical(nullptr,
0204                               i18n("Error starting Krazy2"),
0205                               i18n("No file is active, cannot deduce project."));
0206         return;
0207     }
0208 
0209     KDevelop::IProject *proj = KDevelop::ICore::self()->projectController()->findProjectForUrl(doc->url());
0210     if (!proj) {
0211         QMessageBox::critical(nullptr,
0212                               i18n("Error starting Krazy2"),
0213                               i18n("Cannot find project"));
0214         return;
0215     }
0216 
0217     KSharedConfigPtr ptr = proj->projectConfiguration();
0218     KConfigGroup group = ptr->group("Krazy2");
0219     if (!group.isValid()) {
0220         QMessageBox::critical(nullptr,
0221                               i18n("Error starting Krazy2"),
0222                               i18n("Krazy2 settings need to be set in the project settings"));
0223         return;
0224     }
0225 
0226     if (!group.hasKey("SelectedCheckers")) {
0227         QMessageBox::critical(nullptr,
0228                               i18n("Error starting Krazy2"),
0229                               i18n("No checkers selected"));
0230         return;
0231     }
0232 
0233     QStringList paths;
0234 
0235     if (!allFiles) {
0236         if (!group.hasKey("SelectedPaths")) {
0237             QMessageBox::critical(nullptr,
0238                                   i18n("Error starting Krazy2"),
0239                                   i18n("No paths selected"));
0240             return;
0241         }
0242 
0243         paths = group.readEntry("SelectedPaths",QStringList());
0244         if(paths.isEmpty()) {
0245             QMessageBox::critical(nullptr,
0246                                   i18n("Error starting Krazy2"),
0247                                   i18n("No paths selected"));
0248             return;
0249         }
0250     }
0251     else {
0252         paths.push_back(proj->path().toUrl().toLocalFile());
0253     }
0254 
0255     QStringList csl = group.readEntry("SelectedCheckers", QStringList());
0256     if (csl.isEmpty()) {
0257         QMessageBox::critical(nullptr,
0258                               i18n("Error starting Krazy2"),
0259                               i18n("No checkers selected"));
0260         return;
0261     }
0262 
0263     QList<const Checker*> cl;
0264 
0265     // Build the checkers list from the saves checker filetypes and names
0266     foreach (const QString &s, csl) {
0267         const Checker *c = findChecker(m_checkers, s);
0268         if(c)
0269             cl.push_back(c);
0270     }
0271 
0272     if (cl.isEmpty()) {
0273         QMessageBox::critical(nullptr,
0274                               i18n("Error starting Krazy2"),
0275                               i18n("Unable to restore selected checkers"));
0276         return;
0277     }
0278 
0279     check(paths, cl);
0280 }
0281 
0282 void Krazy2Plugin::check(QStringList paths, QList<const Checker *> &selectedCheckers)
0283 {
0284     auto params = new AnalysisParameters();
0285     params->initializeCheckers(m_checkers);
0286     params->setCheckersToRun(selectedCheckers);
0287     params->setFilesAndDirectories(paths);
0288 
0289     m_results.reset(new AnalysisResults());
0290 
0291     auto job = new AnalysisJob(this);
0292     job->addAnalysisParameters(params);
0293     job->setAnalysisResults(m_results.data());
0294 
0295     connect(job, &KJob::finished, this, &Krazy2Plugin::onAnalysisFinished);
0296 
0297     KDevelop::ICore::self()->runController()->registerJob(job);
0298     m_analysisInProgress = true;
0299 }
0300 
0301 
0302 
0303 #include "krazy2plugin.moc"