1/* This file is part of the KDE project
2 Copyright (C) 2014 Martin Sandsmark <martin.sandsmark@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library 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 GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "replicodeplugin.h"
21#include "replicodeconfigpage.h"
22#include "replicodesettings.h"
23#include "replicodeconfig.h"
24
25#include <QtGlobal>
26#include <QProcess>
27#include <QTemporaryFile>
28#include <QPushButton>
29
30#include <KPluginFactory>
31#include <KPluginLoader>
32#include <KLocale>
33#include <KAction>
34#include <KActionCollection>
35#include <KAboutData>
36#include <kate/application.h>
37#include <QDebug>
38#include <qfileinfo.h>
39#include <kate/mainwindow.h>
40#include <QMessageBox>
41#include <KGlobal>
42#include <KConfigGroup>
43#include <KTextEditor/View>
44#include <KTextEditor/Document>
45#include <QListWidget>
46#include <QVBoxLayout>
47#include <QFormLayout>
48#include <QProcess>
49
50K_PLUGIN_FACTORY(KateReplicodeFactory, registerPlugin<ReplicodePlugin>();)
51K_EXPORT_PLUGIN(KateReplicodeFactory("katereplicodeplugin"))
52
53ReplicodePlugin::ReplicodePlugin(QObject* parent, const QList< QVariant > &args)
54: Kate::Plugin(qobject_cast<Kate::Application*>(parent))
55{
56 Q_UNUSED(args);
57}
58
59ReplicodePlugin::~ReplicodePlugin()
60{
61}
62uint ReplicodePlugin::configPages() const
63{
64 return 1;
65}
66
67QString ReplicodePlugin::configPageFullName(uint number) const
68{
69 Q_ASSERT(number == 0);
70 Q_UNUSED(number);
71 return i18n("Replicode settings");
72}
73
74QString ReplicodePlugin::configPageName(uint number) const
75{
76 Q_ASSERT(number == 0);
77 Q_UNUSED(number);
78 return i18n("Replicode");
79}
80
81
82KIcon ReplicodePlugin::configPageIcon(uint number) const
83{
84 Q_UNUSED(number);
85 return KIcon("code-block");
86}
87
88Kate::PluginConfigPage* ReplicodePlugin::configPage(uint number, QWidget* parent, const char* name)
89{
90 Q_UNUSED(name);
91 Q_UNUSED(number);
92 Q_ASSERT(number == 0);
93 return new ReplicodeConfigPage(parent, name);
94}
95
96ReplicodeView::ReplicodeView(Kate::MainWindow* mainWindow)
97: Kate::PluginView(mainWindow), Kate::XMLGUIClient(KateReplicodeFactory::componentData()), m_mainWindow(mainWindow), m_settingsFile(0), m_executor(0)
98{
99 m_runAction = new KAction(KIcon("code-block"), i18n("Run replicode"), this);
100 m_runAction->setShortcut(Qt::Key_F8);
101 connect(m_runAction, SIGNAL(triggered()), SLOT(runReplicode()));
102 actionCollection()->addAction("katereplicode_run", m_runAction);
103
104 m_stopAction = new KAction(KIcon("process-stop"), i18n("Stop replicode"), this);
105 m_stopAction->setShortcut(Qt::Key_F9);
106 connect(m_stopAction, SIGNAL(triggered()), SLOT(stopReplicode()));
107 actionCollection()->addAction("katereplicode_stop", m_stopAction);
108 m_stopAction->setEnabled(false);
109
110 m_toolview = mainWindow->createToolView("kate_private_plugin_katereplicodeplugin_run", Kate::MainWindow::Bottom, SmallIcon("code-block"), i18n("Replicode Output"));
111 m_replicodeOutput = new QListWidget(m_toolview);
112 m_replicodeOutput->setSelectionMode(QAbstractItemView::ContiguousSelection);
113 connect(m_replicodeOutput, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(outputClicked(QListWidgetItem*)));
114 mainWindow->hideToolView(m_toolview);
115
116 m_configSidebar = mainWindow->createToolView("kate_private_plugin_katereplicodeplugin_config", Kate::MainWindow::Right, SmallIcon("code-block"), i18n("Replicode Config"));
117 m_configView = new ReplicodeConfig(m_configSidebar);
118
119 m_runButton = new QPushButton(i18nc("shortcut for action", "Run (%1)", m_runAction->shortcut().toString()));
120 m_stopButton = new QPushButton(i18nc("shortcut for action", "Stop (%1)", m_stopAction->shortcut().toString()));
121 m_stopButton->setEnabled(false);
122
123 QFormLayout *l = qobject_cast<QFormLayout*>(m_configView->widget(0)->layout());
124 Q_ASSERT(l);
125 l->addRow(m_runButton, m_stopButton);
126 connect(m_runButton, SIGNAL(clicked()), m_runAction, SLOT(trigger()));
127 connect(m_stopButton, SIGNAL(clicked()), m_stopAction, SLOT(trigger()));
128
129 m_mainWindow->guiFactory()->addClient(this);
130 connect(m_mainWindow, SIGNAL(viewChanged()), SLOT(viewChanged()));
131}
132
133ReplicodeView::~ReplicodeView()
134{
135 m_mainWindow->guiFactory()->removeClient(this);
136 delete m_executor;
137 delete m_settingsFile;
138}
139
140void ReplicodeView::viewChanged()
141{
142 if (m_mainWindow->activeView() && m_mainWindow->activeView()->document() && m_mainWindow->activeView()->document()->url().fileName().endsWith(".replicode")) {
143 m_mainWindow->showToolView(m_configSidebar);
144 } else {
145 m_mainWindow->hideToolView(m_configSidebar);
146 m_mainWindow->hideToolView(m_toolview);
147 }
148}
149
150void ReplicodeView::runReplicode()
151{
152 m_mainWindow->showToolView(m_toolview);
153 KTextEditor::View *editor = m_mainWindow->activeView();
154 if (!editor || !editor->document()) {
155 QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Unable to find active file"), i18n("Can't find active file to run!"));
156 return;
157 }
158
159 if (editor->document()->isEmpty()) {
160 QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Empty document"), i18n("Can't execute an empty document"));
161 return;
162 }
163
164 QFileInfo sourceFile = QFileInfo(editor->document()->url().toLocalFile());
165
166 if (!sourceFile.isReadable()) {
167 QMessageBox::warning(m_mainWindow->centralWidget(), i18n("No file"), i18n("Unable to open source file for reading."));
168 return;
169 }
170
171 KConfigGroup config(KGlobal::config(), "Replicode");
172 QString executorPath = config.readEntry<QString>("replicodePath", QString());
173 if (executorPath.isEmpty()) {
174 QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Can't find replicode executor"), i18n("Unable to find replicode executor.\nPlease go to settings and set the path to the Replicode executor."));
175 return;
176 }
177
178 if (m_configView->settingsObject()->userOperatorPath.isEmpty()) {
179 QMessageBox::warning(m_mainWindow->centralWidget(), i18n("Can't find user operator library"), i18n("Unable to find user operator library.\nPlease go to settings and set the path to the library."));
180 }
181
182 if (m_settingsFile) delete m_settingsFile;
183
184 m_settingsFile = new QTemporaryFile;
185 if (!m_settingsFile->open()) {
186 delete m_settingsFile;
187 m_settingsFile = 0;
188 QMessageBox::warning(m_mainWindow->window(), tr("Unable to create file"), tr("Unable to create temporary file!"));
189 }
190 m_configView->settingsObject()->writeXml(m_settingsFile, sourceFile.canonicalFilePath());
191 m_settingsFile->close();
192
193 m_replicodeOutput->clear();
194
195 if (m_executor) delete m_executor;
196 m_executor = new QProcess(this);
197 m_executor->setWorkingDirectory(sourceFile.canonicalPath());
198 connect(m_executor, SIGNAL(readyReadStandardError()), SLOT(gotStderr()));
199 connect(m_executor, SIGNAL(readyReadStandardOutput()), SLOT(gotStdout()));
200 connect(m_executor, SIGNAL(finished(int)), SLOT(replicodeFinished()));
201 connect(m_executor, SIGNAL(error(QProcess::ProcessError)), SLOT(runErrored(QProcess::ProcessError)));
202 qDebug() << executorPath << sourceFile.canonicalPath();
203 m_completed = false;
204 m_executor->start(executorPath, QStringList() << m_settingsFile->fileName(), QProcess::ReadOnly);
205
206 m_runAction->setEnabled(false);
207 m_runButton->setEnabled(false);
208 m_stopAction->setEnabled(true);
209 m_stopButton->setEnabled(true);
210}
211
212void ReplicodeView::stopReplicode()
213{
214 if (m_executor) {
215 m_executor->kill();
216 }
217}
218
219void ReplicodeView::outputClicked(QListWidgetItem *item)
220{
221 QString output = item->text();
222 QStringList pieces = output.split(':');
223
224 if (pieces.length() < 2) return;
225
226 QFileInfo file(pieces[0]);
227 if (!file.isReadable()) return;
228
229 bool ok = false;
230 int lineNumber = pieces[1].toInt(&ok);
231 qDebug() << lineNumber;
232 if (!ok) return;
233
234 KTextEditor::View *doc = m_mainWindow->openUrl(pieces[0]);
235 doc->setCursorPosition(KTextEditor::Cursor(lineNumber, 0));
236 qDebug() << doc->cursorPosition().line();
237}
238
239void ReplicodeView::runErrored(QProcess::ProcessError error)
240{
241 Q_UNUSED(error);
242 QListWidgetItem *item = new QListWidgetItem(i18n("Replicode execution failed: %1", m_executor->errorString()));
243 item->setForeground(Qt::red);
244 m_replicodeOutput->addItem(item);
245 m_replicodeOutput->scrollToBottom();
246 m_completed = true;
247}
248
249void ReplicodeView::replicodeFinished()
250{
251 if (!m_completed) {
252 QListWidgetItem *item = new QListWidgetItem(i18n("Replicode execution finished!"));
253 item->setForeground(Qt::blue);
254 m_replicodeOutput->addItem(item);
255 m_replicodeOutput->scrollToBottom();
256 }
257
258 m_runAction->setEnabled(true);
259 m_runButton->setEnabled(true);
260 m_stopAction->setEnabled(false);
261 m_stopButton->setEnabled(false);
262// delete m_executor;
263// delete m_settingsFile;
264// m_executor = 0;
265// m_settingsFile = 0;
266}
267
268void ReplicodeView::gotStderr()
269{
270 QString output = m_executor->readAllStandardError();
271 foreach(QString line, output.split('\n')) {
272 line = line.simplified();
273 if (line.isEmpty()) continue;
274 QListWidgetItem *item = new QListWidgetItem(line);
275 item->setForeground(Qt::red);
276 m_replicodeOutput->addItem(item);
277 }
278 m_replicodeOutput->scrollToBottom();
279}
280
281void ReplicodeView::gotStdout()
282{
283 QString output = m_executor->readAllStandardOutput();
284 foreach(QString line, output.split('\n')) {
285 line = line.simplified();
286 if (line.isEmpty()) continue;
287 QListWidgetItem *item = new QListWidgetItem(' ' + line);
288 if (line[0] == '>') item->setForeground(Qt::gray);
289 m_replicodeOutput->addItem(item);
290 }
291 m_replicodeOutput->scrollToBottom();
292}
293
294#include "replicodeview.moc"
295#include "replicodeplugin.moc"
296