diff --git a/src/backends/julia/juliaserver/juliaserver.cpp b/src/backends/julia/juliaserver/juliaserver.cpp index 04108ee6..9c904fa4 100644 --- a/src/backends/julia/juliaserver/juliaserver.cpp +++ b/src/backends/julia/juliaserver/juliaserver.cpp @@ -1,133 +1,138 @@ /* * 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 */ #include "juliaserver.h" #include +#include #include #include #include #include #include JuliaServer::JuliaServer(QObject *parent) : QObject(parent) { } JuliaServer::~JuliaServer() { jl_atexit_hook(0); } void JuliaServer::login(const QString &path) const { +#if JULIA_VERSION_MINOR > 5 + jl_init(); +#else QString dir_path = QFileInfo(path).dir().absolutePath(); jl_init(dir_path.toLatin1().constData()); +#endif } void JuliaServer::runJuliaCommand(const QString &command) { // Redirect stdout, stderr to temprorary files QTemporaryFile output, error; if (!output.open() || !error.open()) { qFatal("Unable to create temporary 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() ); // Run command jl_value_t *val = static_cast( jl_eval_string(command.toLatin1().constData()) ); if (jl_exception_occurred()) { // If exception occurred // Show it to user in stderr 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_eval_string("catch_backtrace()") ); jl_value_t *err_stream = static_cast( jl_eval_string("STDERR") ); jl_call3(showerror, err_stream, ex, bt); jl_exception_clear(); m_was_exception = true; } else if (val) { // no exception occurred // If last result is not nothing, show it jl_function_t *equality = jl_get_function(jl_base_module, "=="); jl_value_t *nothing = static_cast(jl_eval_string("nothing")); bool is_nothing = jl_unbox_bool( static_cast(jl_call2(equality, nothing, val)) ); if (!is_nothing) { jl_static_show(JL_STDOUT, val); } m_was_exception = false; } // Clean up streams and files jl_eval_string("flush(STDOUT)"); jl_eval_string("flush(STDERR)"); jl_eval_string("redirect_stdout(__originalSTDOUT__)"); jl_eval_string("redirect_stderr(__originalSTDERR__)"); // Clean up variables auto vars_to_remove = { "__originalSTDOUT__", "__originalSTDERR__" }; for (const auto &var : vars_to_remove) { jl_eval_string( QString::fromLatin1("%1 = 0").arg(QLatin1String(var)) .toLatin1().constData() ); } 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; } diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp index 5ba41715..d6cc6ae8 100644 --- a/src/backends/julia/juliasession.cpp +++ b/src/backends/julia/juliasession.cpp @@ -1,351 +1,353 @@ /* 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 */ #include "juliasession.h" #include #include #include #include #include #include "defaultvariablemodel.h" #include "juliaexpression.h" #include "settings.h" #include "juliahighlighter.h" #include "juliakeywords.h" #include "juliaextensions.h" #include "juliabackend.h" #include "juliacompletionobject.h" +#include JuliaSession::JuliaSession(Cantor::Backend *backend) : Session(backend) , m_process(nullptr) , m_interface(nullptr) , m_currentExpression(nullptr) , m_variableModel(new Cantor::DefaultVariableModel(this)) { } void JuliaSession::login() { emit loginStarted(); 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 (!m_interface->isValid()) { qWarning() << QDBusConnection::sessionBus().lastError().message(); return; } m_interface->call( QString::fromLatin1("login"), JuliaSettings::self()->replPath().path() ); listVariables(); // Plots integration if (integratePlots()) { runJuliaCommand( QLatin1String("import GR; ENV[\"GKS_WSTYPE\"] = \"nul\"") ); } emit loginDone(); + qDebug() << "login to julia " << JULIA_VERSION_STRING << "done"; } void JuliaSession::logout() { m_process->terminate(); JuliaKeywords::instance()->clearVariables(); JuliaKeywords::instance()->clearFunctions(); } 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) { return new JuliaCompletionObject(command, index, this); } QSyntaxHighlighter *JuliaSession::syntaxHighlighter(QObject *parent) { JuliaHighlighter *highlighter = new JuliaHighlighter(parent); QObject::connect( this, SIGNAL(updateHighlighter()), highlighter, SLOT(updateHighlight()) ); return highlighter; } 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); listVariables(); 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 &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 &reply = m_interface->call(QLatin1String("getWasException")); return reply.isValid() && reply.value(); } void JuliaSession::listVariables() { QStringList ignoredVariables; ignoredVariables // These are tech variables of juliaserver << QLatin1String("__originalSTDOUT__") << QLatin1String("__originalSTDERR__"); // Wrapping removed marker to quotes auto rem_marker = QString::fromLatin1("\"%1\"") .arg(JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER); // Clear current symbols JuliaKeywords::instance()->clearVariables(); JuliaKeywords::instance()->clearFunctions(); QStringList processed_modules; // modules we have processed QStringList modules_to_process; // modules in queue modules_to_process << QLatin1String("__GLOBAL__"); // starting from global while (modules_to_process.size() > 0) { // Get from queue auto module = modules_to_process.front(); modules_to_process.pop_front(); if (processed_modules.contains(module)) { continue; } processed_modules << module; // Get whos() output, maybe from cache QString whos_output; if (module == QLatin1String("__GLOBAL__")) { runJuliaCommand(QLatin1String("whos()")); whos_output = getOutput(); } else { auto it = m_whos_cache.find(module); if (it == m_whos_cache.end()) { runJuliaCommand(QString::fromLatin1("whos(%1)").arg(module)); whos_output = getOutput(); m_whos_cache[module] = whos_output; } else { whos_output = it.value(); } } // In this lists we will collect symbols to apply `show` to them // in one DBus call QStringList batchCommands; QStringList batchTypes; QStringList batchNames; for (auto line : whos_output.split(QLatin1String("\n"))) { QString name = line.simplified().split(QLatin1String(" ")).first().simplified(); if (name.isEmpty()) { // some empty line continue; } QString type = line.simplified().split(QLatin1String(" ")).last().simplified(); if (ignoredVariables.contains(name)) { // Ignored variable continue; } if (type == QLatin1String("Module")) { // Found module, place in queue modules_to_process.append(name); continue; } if (type == QLatin1String("Function")) { // Found function JuliaKeywords::instance()->addFunction(name); continue; } if (module != QLatin1String("__GLOBAL__")) { continue; // Don't add variables not included on global scope } // Add to batch batchCommands << QString::fromLatin1("show(%1);").arg(name); batchTypes << type; batchNames << name; } if (batchCommands.isEmpty()) { continue; // nothing to do } // Run batched command runJuliaCommand( batchCommands.join(QLatin1String("print(\"__CANTOR_DELIM__\");")) ); auto values = getOutput().split(QLatin1String("__CANTOR_DELIM__")); for (int i = 0; i < values.size(); i++) { auto value = values[i].simplified(); auto type = batchTypes[i]; auto name = batchNames[i]; if (type == QLatin1String("ASCIIString")) { if (value == rem_marker) { // This is removed variable m_variableModel->removeVariable(name); continue; } } // Register variable m_variableModel->addVariable(name, value); JuliaKeywords::instance()->addVariable(name); } } emit updateHighlighter(); } QAbstractItemModel *JuliaSession::variableModel() { return m_variableModel; } bool JuliaSession::integratePlots() { return JuliaSettings::integratePlots(); }