summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Lakhtanov <ivan.lakhtanov@gmail.com>2016-08-16 01:23:42 (GMT)
committerIvan Lakhtanov <ivan.lakhtanov@gmail.com>2016-08-24 18:44:30 (GMT)
commit7e6af394e4cbf34afd9041dfcfef80c42c1f96c7 (patch)
treeb2eee7e46a73241751d8e0bed5d906f3b8841b7f
parent0d185884ec854f2d51d03afb766e5bcf33196d7d (diff)
Impelemented command execution with DBUS
Used Julia embedding. Features: execution, syntax errors, exceptions, multiline input, printing of the last result. Julia path is detected automatically. BUGS and FIXMES: * Workaround: julia redirect commands user pipes/sockets with limited buffer to store data. So on large output writing pipe end blocks, and julia server execution is blocked. It's better to reimplement this by using concurrent threads to run julia command and read from pipe. Now using files as they provide unlimited buffer. Differential Revision: https://phabricator.kde.org/D2006
-rw-r--r--src/backends/CMakeLists.txt1
-rw-r--r--src/backends/julia/CMakeLists.txt16
-rw-r--r--src/backends/julia/juliabackend.cpp95
-rw-r--r--src/backends/julia/juliabackend.h44
-rw-r--r--src/backends/julia/juliabackend.json12
-rw-r--r--src/backends/julia/juliabackend.kcfg16
-rw-r--r--src/backends/julia/juliaexpression.cpp57
-rw-r--r--src/backends/julia/juliaexpression.h34
-rw-r--r--src/backends/julia/juliaserver/CMakeLists.txt14
-rw-r--r--src/backends/julia/juliaserver/juliaserver.cpp117
-rw-r--r--src/backends/julia/juliaserver/juliaserver.h44
-rw-r--r--src/backends/julia/juliaserver/main.cpp57
-rw-r--r--src/backends/julia/juliasession.cpp191
-rw-r--r--src/backends/julia/juliasession.h69
-rw-r--r--src/backends/julia/settings.kcfgc3
-rw-r--r--src/backends/julia/settings.ui31
16 files changed, 801 insertions, 0 deletions
diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt
index 4987b37..a5b1416 100644
--- a/src/backends/CMakeLists.txt
+++ b/src/backends/CMakeLists.txt
@@ -14,6 +14,7 @@ add_subdirectory(null)
add_subdirectory(maxima)
add_subdirectory(octave)
add_subdirectory(scilab)
+add_subdirectory(julia)
if(NOT WIN32)
add_subdirectory(sage)
diff --git a/src/backends/julia/CMakeLists.txt b/src/backends/julia/CMakeLists.txt
new file mode 100644
index 0000000..8739ac8
--- /dev/null
+++ b/src/backends/julia/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_subdirectory(juliaserver)
+
+set(JuliaBackend_SRCS
+ juliabackend.cpp
+ juliasession.cpp
+ juliaexpression.cpp
+)
+
+kconfig_add_kcfg_files(JuliaBackend_SRCS settings.kcfgc)
+ki18n_wrap_ui(JuliaBackend_SRCS settings.ui)
+
+add_backend(juliabackend ${JuliaBackend_SRCS})
+
+target_link_libraries(cantor_juliabackend Qt5::DBus)
+
+install(FILES juliabackend.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
diff --git a/src/backends/julia/juliabackend.cpp b/src/backends/julia/juliabackend.cpp
new file mode 100644
index 0000000..86679ef
--- /dev/null
+++ b/src/backends/julia/juliabackend.cpp
@@ -0,0 +1,95 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ ---
+ Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#include "juliabackend.h"
+
+#include <klocalizedstring.h>
+
+#include "juliasession.h"
+#include "ui_settings.h"
+#include "settings.h"
+
+JuliaBackend::JuliaBackend(QObject *parent, const QList<QVariant> &args)
+ : Cantor::Backend(parent, args)
+{
+ setEnabled(true);
+}
+
+QString JuliaBackend::id() const
+{
+ return QLatin1String("julia");
+}
+
+Cantor::Session *JuliaBackend::createSession()
+{
+ return new JuliaSession(this);
+}
+
+Cantor::Backend::Capabilities JuliaBackend::capabilities() const
+{
+ return Cantor::Backend::Nothing;
+}
+
+QString JuliaBackend::description() const
+{
+ return i18n(
+ "<p><b>Julia</b> is a high-level, high-performance dynamic programming "
+ "language for technical computing, with syntax that is familiar to "
+ "users of other technical computing environments. It provides a "
+ "sophisticated compiler, distributed parallel execution, numerical "
+ "accuracy, and an extensive mathematical function library.</p>"
+ );
+}
+
+QUrl JuliaBackend::helpUrl() const
+{
+ return QUrl(i18nc(
+ "The url to the documentation of Julia, please check if there is a"
+ " translated version and use the correct url",
+ "http://docs.julialang.org/en/latest/"
+ ));
+}
+
+bool JuliaBackend::requirementsFullfilled() const
+{
+ return QFileInfo(
+ JuliaSettings::self()->replPath().toLocalFile()
+ ).isExecutable();
+}
+
+QWidget *JuliaBackend::settingsWidget(QWidget *parent) const
+{
+ QWidget *widget = new QWidget(parent);
+ Ui::JuliaSettingsBase s;
+ s.setupUi(widget);
+ return widget;
+}
+
+KConfigSkeleton *JuliaBackend::config() const
+{
+ return JuliaSettings::self();
+}
+
+K_PLUGIN_FACTORY_WITH_JSON(
+ juliabackend,
+ "juliabackend.json",
+ registerPlugin<JuliaBackend>();
+)
+
+#include "juliabackend.moc"
diff --git a/src/backends/julia/juliabackend.h b/src/backends/julia/juliabackend.h
new file mode 100644
index 0000000..e208993
--- /dev/null
+++ b/src/backends/julia/juliabackend.h
@@ -0,0 +1,44 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ ---
+ Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#pragma once
+
+#include "backend.h"
+
+class JuliaBackend: public Cantor::Backend
+{
+ Q_OBJECT
+public:
+ explicit JuliaBackend(
+ QObject *parent = 0,
+ const QList<QVariant> &args = QList<QVariant>());
+
+ virtual ~JuliaBackend() {}
+
+ virtual QString id() const override;
+ virtual Cantor::Session *createSession() override;
+
+ virtual Cantor::Backend::Capabilities capabilities() const override;
+ virtual QString description() const override;
+ virtual QUrl helpUrl() const override;
+ virtual bool requirementsFullfilled() const override;
+
+ virtual QWidget *settingsWidget(QWidget *parent) const override;
+ virtual KConfigSkeleton *config() const override;
+};
diff --git a/src/backends/julia/juliabackend.json b/src/backends/julia/juliabackend.json
new file mode 100644
index 0000000..3c7e5ab
--- /dev/null
+++ b/src/backends/julia/juliabackend.json
@@ -0,0 +1,12 @@
+{
+ "KPlugin": {
+ "Dependencies": [],
+ "Description": "Julia backend for Cantor",
+ "Id": "Julia",
+ "Name": "Julia",
+ "ServiceTypes": [
+ "Cantor/Backend"
+ ],
+ "Website": "http://julialang.org/"
+ }
+}
diff --git a/src/backends/julia/juliabackend.kcfg b/src/backends/julia/juliabackend.kcfg
new file mode 100644
index 0000000..e7b6ee1
--- /dev/null
+++ b/src/backends/julia/juliabackend.kcfg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="cantorrc"/>
+ <include>QStandardPaths</include>
+ <group name="JuliaBackend">
+ <entry name="replPath" type="Url">
+ <label>Path to the Julia native REPL</label>
+ <default code="true">
+ QUrl::fromLocalFile(QStandardPaths::findExecutable(QLatin1String("julia")))
+ </default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/src/backends/julia/juliaexpression.cpp b/src/backends/julia/juliaexpression.cpp
new file mode 100644
index 0000000..9ced140
--- /dev/null
+++ b/src/backends/julia/juliaexpression.cpp
@@ -0,0 +1,57 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ ---
+ Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#include "juliaexpression.h"
+
+#include "juliasession.h"
+#include "textresult.h"
+
+JuliaExpression::JuliaExpression(Cantor::Session *session)
+ : Cantor::Expression(session)
+{
+}
+
+void JuliaExpression::evaluate()
+{
+ setStatus(Cantor::Expression::Computing);
+ dynamic_cast<JuliaSession *>(session())->runExpression(this);
+}
+
+void JuliaExpression::finalize()
+{
+ auto juliaSession = dynamic_cast<JuliaSession *>(session());
+ setErrorMessage(
+ juliaSession->getError()
+ .replace(QLatin1String("\n"), QLatin1String("<br>"))
+ );
+ if (juliaSession->getWasException()) {
+ setResult(new Cantor::TextResult(juliaSession->getOutput()));
+ setStatus(Cantor::Expression::Error);
+ } else {
+ setResult(new Cantor::TextResult(juliaSession->getOutput()));
+ setStatus(Cantor::Expression::Done);
+ }
+}
+
+void JuliaExpression::interrupt()
+{
+ setStatus(Cantor::Expression::Interrupted);
+}
+
+#include "juliaexpression.moc"
diff --git a/src/backends/julia/juliaexpression.h b/src/backends/julia/juliaexpression.h
new file mode 100644
index 0000000..5d4fc03
--- /dev/null
+++ b/src/backends/julia/juliaexpression.h
@@ -0,0 +1,34 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ ---
+ Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#pragma once
+
+#include "expression.h"
+
+class JuliaExpression: public Cantor::Expression
+{
+ Q_OBJECT
+public:
+ JuliaExpression(Cantor::Session *session);
+ virtual ~JuliaExpression() {};
+
+ virtual void evaluate() override;
+ virtual void interrupt() override;
+ void finalize();
+};
diff --git a/src/backends/julia/juliaserver/CMakeLists.txt b/src/backends/julia/juliaserver/CMakeLists.txt
new file mode 100644
index 0000000..c6b6771
--- /dev/null
+++ b/src/backends/julia/juliaserver/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(JuliaServer_SRCS
+ juliaserver.cpp
+ main.cpp
+)
+
+add_executable(cantor_juliaserver ${JuliaServer_SRCS})
+
+target_link_libraries(cantor_juliaserver
+ julia
+ Qt5::Widgets
+ Qt5::DBus
+)
+
+install(TARGETS cantor_juliaserver ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/src/backends/julia/juliaserver/juliaserver.cpp b/src/backends/julia/juliaserver/juliaserver.cpp
new file mode 100644
index 0000000..955d570
--- /dev/null
+++ b/src/backends/julia/juliaserver/juliaserver.cpp
@@ -0,0 +1,117 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#include "juliaserver.h"
+
+#include <julia/julia.h>
+
+#include <iostream>
+#include <QFileInfo>
+#include <QDir>
+#include <QTemporaryFile>
+#include <QDebug>
+
+JuliaServer::JuliaServer(QObject *parent)
+ : QObject(parent)
+{
+}
+
+JuliaServer::~JuliaServer()
+{
+ jl_atexit_hook(0);
+}
+
+void JuliaServer::login(const QString &path) const
+{
+ QString dir_path = QFileInfo(path).dir().absolutePath();
+ jl_init(dir_path.toLatin1().constData());
+}
+
+void JuliaServer::runJuliaCommand(const QString &command)
+{
+ QTemporaryFile output, error;
+ if (not output.open() or not error.open()) {
+ qFatal("Unable to create temprorary files for stdout/stderr");
+ return;
+ }
+ jl_eval_string("const originalSTDOUT = STDOUT");
+ jl_eval_string("const originalSTDERR = STDERR");
+ jl_eval_string(
+ QString::fromLatin1("redirect_stdout(open(\"%1\", \"w\"))")
+ .arg(output.fileName()).toLatin1().constData()
+ );
+ jl_eval_string(
+ QString::fromLatin1("redirect_stderr(open(\"%1\", \"w\"))")
+ .arg(error.fileName()).toLatin1().constData()
+ );
+
+ jl_value_t *val = static_cast<jl_value_t *>(
+ jl_eval_string(command.toLatin1().constData())
+ );
+ if (jl_exception_occurred()) {
+ jl_value_t *ex = jl_exception_in_transit;
+ jl_printf(JL_STDERR, "error during run:\n");
+ jl_function_t *showerror =
+ jl_get_function(jl_base_module, "showerror");
+ jl_value_t *bt = static_cast<jl_value_t *>(
+ jl_eval_string("catch_backtrace()")
+ );
+ jl_value_t *err_stream = static_cast<jl_value_t *>(
+ jl_eval_string("STDERR")
+ );
+ jl_call3(showerror, err_stream, ex, bt);
+ jl_exception_clear();
+ m_was_exception = true;
+ } else if (val) {
+ jl_function_t *equality = jl_get_function(jl_base_module, "==");
+ jl_value_t *nothing =
+ static_cast<jl_value_t *>(jl_eval_string("nothing"));
+ bool is_nothing = jl_unbox_bool(
+ static_cast<jl_value_t *>(jl_call2(equality, nothing, val))
+ );
+ if (not is_nothing) {
+ jl_static_show(JL_STDOUT, val);
+ }
+ m_was_exception = false;
+ }
+ jl_eval_string("flush(STDOUT)");
+ jl_eval_string("flush(STDERR)");
+ jl_eval_string("redirect_stdout(originalSTDOUT)");
+ jl_eval_string("redirect_stderr(originalSTDERR)");
+
+ m_output = QString::fromUtf8(output.readAll());
+ m_error = QString::fromUtf8(error.readAll());
+}
+
+QString JuliaServer::getError() const
+{
+ return m_error;
+}
+
+QString JuliaServer::getOutput() const
+{
+ return m_output;
+}
+
+bool JuliaServer::getWasException() const
+{
+ return m_was_exception;
+}
+
+#include "juliaserver.moc"
diff --git a/src/backends/julia/juliaserver/juliaserver.h b/src/backends/julia/juliaserver/juliaserver.h
new file mode 100644
index 0000000..0cb16e2
--- /dev/null
+++ b/src/backends/julia/juliaserver/juliaserver.h
@@ -0,0 +1,44 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com
+ */
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+class JuliaServer: public QObject
+{
+ Q_OBJECT
+public:
+ JuliaServer(QObject *parent = nullptr);
+
+ virtual ~JuliaServer();
+
+public Q_SLOTS:
+ Q_SCRIPTABLE void login(const QString &path) const;
+ Q_SCRIPTABLE void runJuliaCommand(const QString &command);
+ Q_SCRIPTABLE QString getOutput() const;
+ Q_SCRIPTABLE QString getError() const;
+ Q_SCRIPTABLE bool getWasException() const;
+
+private:
+ QString m_error;
+ QString m_output;
+ bool m_was_exception;
+};
diff --git a/src/backends/julia/juliaserver/main.cpp b/src/backends/julia/juliaserver/main.cpp
new file mode 100644
index 0000000..ad7e4d9
--- /dev/null
+++ b/src/backends/julia/juliaserver/main.cpp
@@ -0,0 +1,57 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+
+#include <QApplication>
+#include <QDBusConnection>
+#include <QDBusError>
+#include <QDebug>
+#include <QTextStream>
+
+#include "juliaserver.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ if (not QDBusConnection::sessionBus().isConnected()) {
+ qWarning() << "Can't connect to the D-Bus session bus.\n"
+ "To start it, run: eval `dbus-launch --auto-syntax`";
+ return 1;
+ }
+
+ const QString &serviceName =
+ QString::fromLatin1("org.kde.Cantor.Julia-%1").arg(app.applicationPid());
+
+ if (not QDBusConnection::sessionBus().registerService(serviceName)) {
+ qWarning() << QDBusConnection::sessionBus().lastError().message();
+ return 2;
+ }
+
+ JuliaServer server;
+ QDBusConnection::sessionBus().registerObject(
+ QLatin1String("/"),
+ &server,
+ QDBusConnection::ExportAllSlots
+ );
+
+ QTextStream(stdout) << "ready" << endl;
+
+ return app.exec();
+}
diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp
new file mode 100644
index 0000000..e1f84b9
--- /dev/null
+++ b/src/backends/julia/juliasession.cpp
@@ -0,0 +1,191 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ ---
+ Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#include "juliasession.h"
+
+#include <KProcess>
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusReply>
+#include <QStandardPaths>
+
+#include "juliaexpression.h"
+#include "settings.h"
+
+JuliaSession::JuliaSession(Cantor::Backend *backend)
+ : Session(backend)
+ , m_process(nullptr)
+ , m_interface(nullptr)
+ , m_currentExpression(nullptr)
+{
+}
+
+void JuliaSession::login()
+{
+ if (m_process) {
+ m_process->deleteLater();
+ }
+
+ m_process = new KProcess(this);
+ m_process->setOutputChannelMode(KProcess::SeparateChannels);
+
+ (*m_process)
+ << QStandardPaths::findExecutable(QLatin1String("cantor_juliaserver"));
+
+ m_process->start();
+
+ m_process->waitForStarted();
+ m_process->waitForReadyRead();
+ QTextStream stream(m_process->readAllStandardOutput());
+
+ QString readyStatus = QLatin1String("ready");
+ while (m_process->state() == QProcess::Running) {
+ const QString &rl = stream.readLine();
+ if (rl == readyStatus) {
+ break;
+ }
+ }
+
+ if (!QDBusConnection::sessionBus().isConnected()) {
+ qWarning() << "Can't connect to the D-Bus session bus.\n"
+ "To start it, run: eval `dbus-launch --auto-syntax`";
+ return;
+ }
+
+ const QString &serviceName =
+ QString::fromLatin1("org.kde.Cantor.Julia-%1").arg(m_process->pid());
+
+ m_interface = new QDBusInterface(
+ serviceName,
+ QString::fromLatin1("/"),
+ QString(),
+ QDBusConnection::sessionBus()
+ );
+
+ if (not m_interface->isValid()) {
+ qWarning() << QDBusConnection::sessionBus().lastError().message();
+ return;
+ }
+
+ m_interface->call(
+ QString::fromLatin1("login"),
+ JuliaSettings::self()->replPath().path()
+ );
+
+ emit ready();
+}
+
+void JuliaSession::logout()
+{
+ m_process->terminate();
+ changeStatus(Cantor::Session::Done);
+}
+
+void JuliaSession::interrupt()
+{
+ if (m_process->pid()) {
+ m_process->kill();
+ }
+
+ for (Cantor::Expression *e : m_runningExpressions) {
+ e->interrupt();
+ }
+
+ m_runningExpressions.clear();
+ changeStatus(Cantor::Session::Done);
+}
+
+Cantor::Expression *JuliaSession::evaluateExpression(
+ const QString &cmd,
+ Cantor::Expression::FinishingBehavior behave)
+{
+ JuliaExpression *expr = new JuliaExpression(this);
+
+ changeStatus(Cantor::Session::Running);
+
+ expr->setFinishingBehavior(behave);
+ expr->setCommand(cmd);
+ expr->evaluate();
+
+ return expr;
+}
+
+Cantor::CompletionObject *JuliaSession::completionFor(
+ const QString &command,
+ int index)
+{
+ Q_UNUSED(command);
+ Q_UNUSED(index);
+ return nullptr;
+}
+
+void JuliaSession::runJuliaCommand(const QString &command) const
+{
+ m_interface->call(QLatin1String("runJuliaCommand"), command);
+}
+
+void JuliaSession::runJuliaCommandAsync(const QString &command)
+{
+ m_interface->callWithCallback(
+ QLatin1String("runJuliaCommand"),
+ {command},
+ this,
+ SLOT(onResultReady())
+ );
+}
+
+void JuliaSession::onResultReady()
+{
+ m_currentExpression->finalize();
+ m_runningExpressions.removeAll(m_currentExpression);
+
+ changeStatus(Cantor::Session::Done);
+}
+
+void JuliaSession::runExpression(JuliaExpression *expr)
+{
+ m_runningExpressions.append(expr);
+ m_currentExpression = expr;
+ runJuliaCommandAsync(expr->command());
+}
+
+QString JuliaSession::getStringFromServer(const QString &method)
+{
+ const QDBusReply<QString> &reply = m_interface->call(method);
+ return (reply.isValid() ? reply.value() : reply.error().message());
+}
+
+QString JuliaSession::getOutput()
+{
+ return getStringFromServer(QLatin1String("getOutput"));
+}
+
+QString JuliaSession::getError()
+{
+ return getStringFromServer(QLatin1String("getError"));
+}
+
+bool JuliaSession::getWasException()
+{
+ const QDBusReply<bool> &reply =
+ m_interface->call(QLatin1String("getWasException"));
+ return reply.isValid() and reply.value();
+}
+
+#include "juliasession.moc"
diff --git a/src/backends/julia/juliasession.h b/src/backends/julia/juliasession.h
new file mode 100644
index 0000000..9c8a40c
--- /dev/null
+++ b/src/backends/julia/juliasession.h
@@ -0,0 +1,69 @@
+/*
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ ---
+ Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#pragma once
+
+#include "session.h"
+
+class JuliaExpression;
+class KProcess;
+class QDBusInterface;
+
+class JuliaSession: public Cantor::Session
+{
+ Q_OBJECT
+public:
+ JuliaSession(Cantor::Backend *backend);
+ virtual ~JuliaSession() {}
+
+ virtual void login() override;
+ virtual void logout() override;
+
+ virtual void interrupt() override;
+
+ virtual Cantor::Expression *evaluateExpression(
+ const QString &command,
+ Cantor::Expression::FinishingBehavior behave) override;
+
+ virtual Cantor::CompletionObject *completionFor(
+ const QString &cmd,
+ int index = -1) override;
+
+private Q_SLOTS:
+ void onResultReady();
+
+private:
+ KProcess *m_process;
+ QDBusInterface *m_interface;
+
+ QList<JuliaExpression *> m_runningExpressions;
+ JuliaExpression *m_currentExpression;
+
+ friend JuliaExpression;
+
+ void runExpression(JuliaExpression *expression);
+
+ void runJuliaCommand(const QString &command) const;
+ void runJuliaCommandAsync(const QString &command);
+
+ QString getStringFromServer(const QString &method);
+ QString getOutput();
+ QString getError();
+ bool getWasException();
+};
diff --git a/src/backends/julia/settings.kcfgc b/src/backends/julia/settings.kcfgc
new file mode 100644
index 0000000..493ddf5
--- /dev/null
+++ b/src/backends/julia/settings.kcfgc
@@ -0,0 +1,3 @@
+File=juliabackend.kcfg
+ClassName=JuliaSettings
+Singleton=true
diff --git a/src/backends/julia/settings.ui b/src/backends/julia/settings.ui
new file mode 100644
index 0000000..f978d03
--- /dev/null
+++ b/src/backends/julia/settings.ui
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>JuliaSettingsBase</class>
+ <widget class="QWidget" name="JuliaSettingsBase">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Path to Julia REPL:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="KUrlRequester" name="kcfg_replPath"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KUrlRequester</class>
+ <extends>QFrame</extends>
+ <header>kurlrequester.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>