1 /*************************************************************************************
2 * Copyright (C) 2016 by Carlos Nihelton <carlosnsoliveira@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU General Public License *
6 * as published by the Free Software Foundation; either version 2 *
7 * of the License, or (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA *
17 * *
18 * Now let's read a beautiful song: *
19
20 夢のつづき
21 追いかけていたはずなのに
22
23 曲がりくねった
24 細い道 人につまずく
25
26 あの頃みたいにって
27 戻りたい訳じゃないの
28
29 無くしてきた空を
30 探してる
31
32 わかってくれますように
33
34 犠牲になったような
35 悲しい顔はやめてよ
36
37 罪の最後は涙じゃないよ
38 ずっと苦しく背負ってくんだ
39
40 出口見えない感情迷路に
41 誰を待ってるの?
42
43 白いノートに綴ったように
44 もっと素直に吐き出したいよ
45
46 何から
47 逃れたいんだ
48
49 …現実ってやつ?
50
51 叶えるために
52 生きてるんだって
53
54 忘れちゃいそうな
55 夜の真ん中
56
57 無難になんて
58 やってられないから
59
60 …帰る場所もないの
61
62 この想いを 消してしまうには
63 まだ人生長いでしょ?(I'm on the way)
64
65 懐かしくなる
66 こんな痛みも歓迎じゃん
67
68 謝らなくちゃいけないよね
69 ah ごめんね
70
71 うまく言えなくて
72 心配かけたままだったね
73
74 あの日かかえた全部
75 あしたかかえる全部
76
77 順番つけたりは
78 しないから
79
80 わかってくれますように
81
82 そっと目を閉じたんだ
83 見たくないものまで
84 見えんだもん
85
86 いらないウワサにちょっと
87 初めて聞く発言どっち?
88
89 2回会ったら友達だって??
90 ウソはやめてね
91
92 赤いハートが苛立つように
93 身体ん中燃えているんだ
94
95 ホントは
96 期待してんの
97
98 …現実ってやつ?
99
100 叶えるために
101 生きてるんだって
102
103 叫びたくなるよ
104 聞こえていますか?
105
106 無難になんて
107 やってられないから
108
109 …帰る場所もないの
110
111 優しさには いつも感謝してる
112 だから強くなりたい(I'm on the way)
113
114 進むために
115 敵も味方も歓迎じゃん
116
117 どうやって次のドア
118 開けるんだっけ?考えてる?
119
120 もう引き返せない
121 物語 始まってるんだ
122
123 目を覚ませ 目を覚ませ
124
125 この想いを 消してしまうには
126 まだ人生長いでしょ?
127
128 やり残してるコト
129 やり直してみたいから
130
131 もう一度ゆこう
132
133 叶えるために
134 生きてるんだって
135
136 叫びたくなるよ
137 聞こえていますか?
138
139 無難になんて
140 やってられないから
141
142 …帰る場所もないの
143
144 優しさには いつも感謝してる
145 だから強くなりたい(I'm on the way)
146
147 懐かしくなる
148 こんな痛みも歓迎じゃん
149
150 *************************************************************************************/
151
152 #include <unistd.h>
153
154 #include <QAction>
155 #include <QMessageBox>
156
157 #include <kactioncollection.h>
158 #include <klocalizedstring.h>
159 #include <kpluginfactory.h>
160 #include <kpluginloader.h>
161 #include <kprocess.h>
162
163 #include <execute/iexecuteplugin.h>
164
165 #include <KXMLGUIFactory>
166 #include <interfaces/icore.h>
167 #include <interfaces/idebugcontroller.h>
168 #include <interfaces/idocument.h>
169 #include <interfaces/idocumentcontroller.h>
170 #include <interfaces/ilanguagecontroller.h>
171 #include <interfaces/iplugincontroller.h>
172 #include <interfaces/iproject.h>
173 #include <interfaces/iprojectcontroller.h>
174 #include <interfaces/iruncontroller.h>
175 #include <interfaces/iuicontroller.h>
176 #include <interfaces/launchconfigurationtype.h>
177 #include <language/interfaces/editorcontext.h>
178 #include <project/interfaces/ibuildsystemmanager.h>
179 #include <project/projectconfigpage.h>
180 #include <project/projectmodel.h>
181 #include <shell/problemmodelset.h>
182 #include <util/executecompositejob.h>
183
184 #include "./config/clangtidypreferences.h"
185 #include "./config/perprojectconfigpage.h"
186 #include "debug.h"
187 #include "job.h"
188 #include "plugin.h"
189
190 using namespace KDevelop;
191
192 K_PLUGIN_FACTORY_WITH_JSON(ClangTidyFactory, "res/kdevclangtidy.json", registerPlugin<ClangTidy::Plugin>();)
193 namespace ClangTidy
194 {
Plugin(QObject * parent,const QVariantList &)195 Plugin::Plugin(QObject* parent, const QVariantList& /*unused*/)
196 : IPlugin("kdevclangtidy", parent)
197 , m_model(new KDevelop::ProblemModel(parent))
198 {
199 qCDebug(KDEV_CLANGTIDY) << "setting clangtidy rc file";
200 setXMLFile("kdevclangtidy.rc");
201
202 QAction* act_checkfile;
203 act_checkfile = actionCollection()->addAction("clangtidy_file", this, SLOT(runClangTidyFile()));
204 act_checkfile->setStatusTip(i18n("Launches ClangTidy for current file"));
205 act_checkfile->setText(i18n("clang-tidy"));
206
207 /* TODO: Uncomment this only when discover a safe way to run clang-tidy on the whole project.
208 // QAction* act_check_all_files;
209 // act_check_all_files = actionCollection()->addAction ( "clangtidy_all", this, SLOT ( runClangTidyAll() ) );
210 // act_check_all_files->setStatusTip ( i18n ( "Launches clangtidy for all translation "
211 // "units of current project" ) );
212 // act_check_all_files->setText ( i18n ( "clang-tidy (all)" ) );
213 */
214
215 IExecutePlugin* iface = KDevelop::ICore::self()
216 ->pluginController()
217 ->pluginForExtension("org.kdevelop.IExecutePlugin")
218 ->extension<IExecutePlugin>();
219 Q_ASSERT(iface);
220
221 ProblemModelSet* pms = core()->languageController()->problemModelSet();
222 pms->addModel(QStringLiteral("ClangTidy"), m_model.data());
223
224 m_config = KSharedConfig::openConfig()->group("ClangTidy");
225 auto clangtidyPath = m_config.readEntry(ConfigGroup::ExecutablePath);
226
227 // TODO(cnihelton): auto detect clang-tidy executable instead of hard-coding it.
228 if (clangtidyPath.isEmpty()) {
229 clangtidyPath = QString("/usr/bin/clang-tidy");
230 }
231
232 collectAllAvailableChecks(clangtidyPath);
233
234 m_config.writeEntry(ConfigGroup::AdditionalParameters, "");
235 for (auto check : m_allChecks) {
236 bool enable = check.contains("cert") || check.contains("-core.") || check.contains("-cplusplus")
237 || check.contains("-deadcode") || check.contains("-security") || check.contains("cppcoreguide");
238 if (enable) {
239 m_activeChecks << check;
240 } else {
241 m_activeChecks.removeAll(check);
242 }
243 }
244 m_activeChecks.removeDuplicates();
245 m_config.writeEntry(ConfigGroup::EnabledChecks, m_activeChecks.join(','));
246 }
247
unload()248 void Plugin::unload()
249 {
250 ProblemModelSet* pms = core()->languageController()->problemModelSet();
251 pms->removeModel(QStringLiteral("ClangTidy"));
252 }
253
collectAllAvailableChecks(QString clangtidyPath)254 void Plugin::collectAllAvailableChecks(QString clangtidyPath)
255 {
256 m_allChecks.clear();
257 KProcess tidy;
258 tidy << clangtidyPath << QLatin1String("-checks=*") << QLatin1String("--list-checks");
259 tidy.setOutputChannelMode(KProcess::OnlyStdoutChannel);
260 tidy.start();
261
262 if (!tidy.waitForStarted()) {
263 qCDebug(KDEV_CLANGTIDY) << "Unable to execute clang-tidy.";
264 return;
265 }
266
267 tidy.closeWriteChannel();
268 if (!tidy.waitForFinished()) {
269 qCDebug(KDEV_CLANGTIDY) << "Failed during clang-tidy execution.";
270 return;
271 }
272
273 QTextStream ios(&tidy);
274 QString each;
275 while (ios.readLineInto(&each)) {
276 m_allChecks.append(each.trimmed());
277 }
278 if (m_allChecks.size() > 3) {
279 m_allChecks.removeAt(m_allChecks.length() - 1);
280 m_allChecks.removeAt(0);
281 }
282 m_allChecks.removeDuplicates();
283 }
284
runClangTidy(bool allFiles)285 void Plugin::runClangTidy(bool allFiles)
286 {
287 KDevelop::IDocument* doc = core()->documentController()->activeDocument();
288 if (!doc) {
289 QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"),
290 i18n("No suitable active file, unable to deduce project."));
291 return;
292 }
293
294 KDevelop::IProject* project = core()->projectController()->findProjectForUrl(doc->url());
295 if (!project) {
296 QMessageBox::critical(nullptr, i18n("Error starting clang-tidy"), i18n("Active file isn't in a project"));
297 return;
298 }
299
300 m_config = project->projectConfiguration()->group("ClangTidy");
301 if (!m_config.isValid()) {
302 QMessageBox::critical(nullptr, i18n("Error starting ClangTidy"),
303 i18n("Can't load parameters. They must be set in the project settings."));
304 return;
305 }
306
307 auto clangtidyPath = m_config.readEntry(ConfigGroup::ExecutablePath);
308 auto buildSystem = project->buildSystemManager();
309
310 Job::Parameters params;
311
312 params.projectRootDir = project->path().toLocalFile();
313
314 // TODO: auto detect clang-tidy executable instead of hard-coding it.
315 if (clangtidyPath.isEmpty()) {
316 params.executablePath = QStringLiteral("/usr/bin/clang-tidy");
317 } else {
318 params.executablePath = clangtidyPath;
319 }
320
321 if (allFiles) {
322 params.filePath = project->path().toUrl().toLocalFile();
323 } else {
324 params.filePath = doc->url().toLocalFile();
325 }
326 params.buildDir = buildSystem->buildDirectory(project->projectItem()).toLocalFile();
327 params.additionalParameters = m_config.readEntry(ConfigGroup::AdditionalParameters);
328 params.analiseTempDtors = m_config.readEntry(ConfigGroup::AnaliseTempDtors);
329 params.enabledChecks = m_activeChecks.join(',');
330 params.useConfigFile = m_config.readEntry(ConfigGroup::UseConfigFile);
331 params.dumpConfig = m_config.readEntry(ConfigGroup::DumpConfig);
332 params.enableChecksProfile = m_config.readEntry(ConfigGroup::EnableChecksProfile);
333 params.exportFixes = m_config.readEntry(ConfigGroup::ExportFixes);
334 params.extraArgs = m_config.readEntry(ConfigGroup::ExtraArgs);
335 params.extraArgsBefore = m_config.readEntry(ConfigGroup::ExtraArgsBefore);
336 params.autoFix = m_config.readEntry(ConfigGroup::AutoFix);
337 params.headerFilter = m_config.readEntry(ConfigGroup::HeaderFilter);
338 params.lineFilter = m_config.readEntry(ConfigGroup::LineFilter);
339 params.listChecks = m_config.readEntry(ConfigGroup::ListChecks);
340 params.checkSystemHeaders = m_config.readEntry(ConfigGroup::CheckSystemHeaders);
341
342 if (!params.dumpConfig.isEmpty()) {
343 Job* job = new ClangTidy::Job(params, this);
344 core()->runController()->registerJob(job);
345 params.dumpConfig = QString();
346 }
347 Job* job2 = new ClangTidy::Job(params, this);
348 connect(job2, SIGNAL(finished(KJob*)), this, SLOT(result(KJob*)));
349 core()->runController()->registerJob(job2);
350 }
351
runClangTidyFile()352 void Plugin::runClangTidyFile()
353 {
354 bool allFiles = false;
355 runClangTidy(allFiles);
356 }
357
runClangTidyAll()358 void Plugin::runClangTidyAll()
359 {
360 bool allFiles = true;
361 runClangTidy(allFiles);
362 }
363
loadOutput()364 void Plugin::loadOutput()
365 {
366 }
367
result(KJob * job)368 void Plugin::result(KJob* job)
369 {
370 Job* aj = dynamic_cast<Job*>(job);
371 if (!aj) {
372 return;
373 }
374
375 if (aj->status() == KDevelop::OutputExecuteJob::JobStatus::JobSucceeded) {
376 m_model->setProblems(aj->problems());
377
378 core()->uiController()->findToolView(i18nd("kdevproblemreporter", "Problems"), 0,
379 KDevelop::IUiController::FindFlags::Raise);
380 }
381 }
382
contextMenuExtension(KDevelop::Context * context)383 KDevelop::ContextMenuExtension Plugin::contextMenuExtension(KDevelop::Context* context)
384 {
385 KDevelop::IDocument* doc = core()->documentController()->activeDocument();
386 KDevelop::ContextMenuExtension extension = KDevelop::IPlugin::contextMenuExtension(context);
387
388 if (context->type() == KDevelop::Context::EditorContext) {
389
390 auto mime = doc->mimeType().name();
391 if (mime == QLatin1String("text/x-c++src") || mime == QLatin1String("text/x-csrc")) {
392 QAction* action
393 = new QAction(QIcon::fromTheme("document-new"), i18n("Check current unit with clang-tidy"), this);
394 connect(action, SIGNAL(triggered(bool)), this, SLOT(runClangTidyFile()));
395 extension.addAction(KDevelop::ContextMenuExtension::AnalyzeGroup, action);
396 }
397 }
398 return extension;
399 }
400
perProjectConfigPage(int number,const ProjectConfigOptions & options,QWidget * parent)401 KDevelop::ConfigPage* Plugin::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent)
402 {
403 if (number != 0) {
404 return nullptr;
405 } else {
406 auto config = new PerProjectConfigPage(options.project, parent);
407 config->setActiveChecksReceptorList(&m_activeChecks);
408 config->setList(m_allChecks);
409 return config;
410 }
411 }
412
configPage(int number,QWidget * parent)413 KDevelop::ConfigPage* Plugin::configPage(int number, QWidget* parent)
414 {
415 if (number != 0) {
416 return nullptr;
417 } else {
418 return new ClangTidyPreferences(this, parent);
419 }
420 }
421 }
422
423 #include "plugin.moc"
424