1 /*
2 SPDX-FileCopyrightText: 2008 Cédric Pasteur <cedric.pasteur@free.fr>
3 SPDX-FileCopyrightText: 2009 Andreas Pakulat <apaku@gmx.de>
4 SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9 #include "sourceformatterjob.h"
10
11 #include "sourceformattercontroller.h"
12
13 #include <debug.h>
14
15 #include <QMimeDatabase>
16 #include <QTextStream>
17
18 #include <KIO/StoredTransferJob>
19 #include <KLocalizedString>
20
21 #include <interfaces/icore.h>
22 #include <interfaces/iuicontroller.h>
23 #include <interfaces/idocumentcontroller.h>
24 #include <interfaces/isourceformatter.h>
25 #include <sublime/message.h>
26
27 using namespace KDevelop;
28
29
SourceFormatterJob(SourceFormatterController * sourceFormatterController)30 SourceFormatterJob::SourceFormatterJob(SourceFormatterController* sourceFormatterController)
31 : KJob(sourceFormatterController)
32 , m_sourceFormatterController(sourceFormatterController)
33 , m_workState(WorkIdle)
34 , m_fileIndex(0)
35 {
36 setCapabilities(Killable);
37 // set name for job listing
38 setObjectName(i18n("Reformatting"));
39
40 KDevelop::ICore::self()->uiController()->registerStatus(this);
41
42 connect(this, &SourceFormatterJob::finished, this, [this]() {
43 emit hideProgress(this);
44 });
45 }
46
statusName() const47 QString SourceFormatterJob::statusName() const
48 {
49 return i18n("Reformat Files");
50 }
51
doWork()52 void SourceFormatterJob::doWork()
53 {
54 // TODO: consider to use ExecuteCompositeJob, with every file a separate subjob
55 switch (m_workState) {
56 case WorkIdle:
57 m_workState = WorkFormat;
58 m_fileIndex = 0;
59 emit showProgress(this, 0, 0, 0);
60 emit showMessage(this, i18np("Reformatting one file",
61 "Reformatting %1 files",
62 m_fileList.length()));
63
64 QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
65 break;
66 case WorkFormat:
67 if (m_fileIndex < m_fileList.length()) {
68 emit showProgress(this, 0, m_fileList.length(), m_fileIndex);
69 formatFile(m_fileList[m_fileIndex]);
70
71 // trigger formatting of next file
72 ++m_fileIndex;
73 QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
74 } else {
75 m_workState = WorkIdle;
76 emitResult();
77 }
78 break;
79 case WorkCancelled:
80 break;
81 }
82 }
83
start()84 void SourceFormatterJob::start()
85 {
86 if (m_workState != WorkIdle)
87 return;
88
89 m_workState = WorkIdle;
90
91 QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
92 }
93
doKill()94 bool SourceFormatterJob::doKill()
95 {
96 m_workState = WorkCancelled;
97 return true;
98 }
99
setFiles(const QList<QUrl> & fileList)100 void SourceFormatterJob::setFiles(const QList<QUrl>& fileList)
101 {
102 m_fileList = fileList;
103 }
104
formatFile(const QUrl & url)105 void SourceFormatterJob::formatFile(const QUrl& url)
106 {
107 // check mimetype
108 QMimeType mime = QMimeDatabase().mimeTypeForUrl(url);
109 qCDebug(SHELL) << "Checking file " << url << " of mime type " << mime.name();
110 auto formatter = m_sourceFormatterController->formatterForUrl(url, mime);
111 if (!formatter) // unsupported mime type
112 return;
113
114 // if the file is opened in the editor, format the text in the editor without saving it
115 auto doc = ICore::self()->documentController()->documentForUrl(url);
116 if (doc) {
117 qCDebug(SHELL) << "Processing file " << url << "opened in editor";
118 m_sourceFormatterController->formatDocument(doc, formatter, mime);
119 return;
120 }
121
122 qCDebug(SHELL) << "Processing file " << url;
123 auto getJob = KIO::storedGet(url);
124 // TODO: make also async and use start() and integrate using setError and setErrorString.
125 if (getJob->exec()) {
126 // TODO: really fromLocal8Bit/toLocal8Bit? no encoding detection? added in b8062f736a2bf2eec098af531a7fda6ebcdc7cde
127 QString text = QString::fromLocal8Bit(getJob->data());
128 text = formatter->formatSource(text, url, mime);
129 text = m_sourceFormatterController->addModelineForCurrentLang(text, url, mime);
130
131 auto putJob = KIO::storedPut(text.toLocal8Bit(), url, -1, KIO::Overwrite);
132 // see getJob
133 if (!putJob->exec()) {
134 auto* message = new Sublime::Message(putJob->errorString(), Sublime::Message::Error);
135 ICore::self()->uiController()->postMessage(message);
136 }
137 } else {
138 auto* message = new Sublime::Message(getJob->errorString(), Sublime::Message::Error);
139 ICore::self()->uiController()->postMessage(message);
140 }
141 }
142