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