diff --git a/src/backends/julia/CMakeLists.txt b/src/backends/julia/CMakeLists.txt
--- a/src/backends/julia/CMakeLists.txt
+++ b/src/backends/julia/CMakeLists.txt
@@ -7,6 +7,7 @@
juliaexpression.cpp
juliakeywords.cpp
juliahighlighter.cpp
+ juliaextensions.cpp
)
kconfig_add_kcfg_files(JuliaBackend_SRCS settings.kcfgc)
diff --git a/src/backends/julia/julia.qrc b/src/backends/julia/julia.qrc
--- a/src/backends/julia/julia.qrc
+++ b/src/backends/julia/julia.qrc
@@ -3,4 +3,9 @@
keywords.xml
+
+ variables_cleaner.jl
+ variables_loader.jl
+ variables_saver.jl
+
diff --git a/src/backends/julia/juliabackend.cpp b/src/backends/julia/juliabackend.cpp
--- a/src/backends/julia/juliabackend.cpp
+++ b/src/backends/julia/juliabackend.cpp
@@ -24,11 +24,14 @@
#include "juliasession.h"
#include "ui_settings.h"
#include "settings.h"
+#include "juliaextensions.h"
JuliaBackend::JuliaBackend(QObject *parent, const QList &args)
: Cantor::Backend(parent, args)
{
setEnabled(true);
+
+ new JuliaVariableManagementExtension(this);
}
QString JuliaBackend::id() const
@@ -43,7 +46,8 @@
Cantor::Backend::Capabilities JuliaBackend::capabilities() const
{
- return Cantor::Backend::SyntaxHighlighting;
+ return Cantor::Backend::SyntaxHighlighting |
+ Cantor::Backend::VariableManagement;
}
QString JuliaBackend::description() const
diff --git a/src/backends/julia/juliaextensions.h b/src/backends/julia/juliaextensions.h
new file mode 100644
--- /dev/null
+++ b/src/backends/julia/juliaextensions.h
@@ -0,0 +1,47 @@
+/*
+ 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
+ */
+#pragma once
+
+#include
+
+#define JULIA_EXT_CDTOR_DECL(name) Julia##name##Extension(QObject *parent); \
+ ~Julia##name##Extension();
+
+
+class JuliaVariableManagementExtension: public Cantor::VariableManagementExtension
+{
+public:
+ JULIA_EXT_CDTOR_DECL(VariableManagement)
+
+ static const QString REMOVED_VARIABLE_MARKER;
+
+ virtual QString addVariable(
+ const QString &name,
+ const QString &value) override;
+
+ virtual QString setValue(
+ const QString &name,
+ const QString &value) override;
+
+ virtual QString removeVariable(const QString &name) override;
+ virtual QString saveVariables(const QString &fileName) override;
+ virtual QString loadVariables(const QString &fileName) override;
+ virtual QString clearVariables() override;
+};
diff --git a/src/backends/julia/juliaextensions.cpp b/src/backends/julia/juliaextensions.cpp
new file mode 100644
--- /dev/null
+++ b/src/backends/julia/juliaextensions.cpp
@@ -0,0 +1,72 @@
+/*
+ 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 "juliaextensions.h"
+
+#include
+#include
+
+#include "juliautils.h"
+
+#define JULIA_EXT_CDTOR(name) Julia##name##Extension::Julia##name##Extension(QObject *parent) : name##Extension(parent) {} \
+ Julia##name##Extension::~Julia##name##Extension() {}
+
+
+JULIA_EXT_CDTOR(VariableManagement)
+
+const QString JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER =
+ QLatin1String("__REM__");
+
+QString JuliaVariableManagementExtension::addVariable(
+ const QString &name,
+ const QString &value)
+{
+ return setValue(name, value);
+}
+
+QString JuliaVariableManagementExtension::setValue(
+ const QString &name,
+ const QString &value)
+{
+ return QString::fromLatin1("%1 = %2").arg(name).arg(value);
+}
+
+QString JuliaVariableManagementExtension::removeVariable(const QString &name)
+{
+ // There is no way to completely delete object from scope:
+ // http://docs.julialang.org/en/release-0.4/manual/faq/#how-do-i-delete-an-object-in-memory
+ return QString::fromLatin1("%1 = \"%2\"")
+ .arg(name).arg(REMOVED_VARIABLE_MARKER);
+}
+
+QString JuliaVariableManagementExtension::clearVariables()
+{
+ return fromSource(QLatin1String(":jl/variables_cleaner.jl"))
+ .arg(REMOVED_VARIABLE_MARKER);
+}
+
+QString JuliaVariableManagementExtension::saveVariables(const QString &fileName)
+{
+ return fromSource(QLatin1String(":jl/variables_saver.jl")).arg(fileName);
+}
+
+QString JuliaVariableManagementExtension::loadVariables(const QString &fileName)
+{
+ return fromSource(QLatin1String(":jl/variables_loader.jl")).arg(fileName);
+}
diff --git a/src/backends/julia/juliahighlighter.cpp b/src/backends/julia/juliahighlighter.cpp
--- a/src/backends/julia/juliahighlighter.cpp
+++ b/src/backends/julia/juliahighlighter.cpp
@@ -27,11 +27,9 @@
JuliaHighlighter::JuliaHighlighter(QObject *parent)
: Cantor::DefaultHighlighter(parent)
{
- addRule(QRegExp(QLatin1String("\\b\\w+(?=\\()")), functionFormat());
-
- // Code highlighting the different keywords
addKeywords(JuliaKeywords::instance()->keywords());
addVariables(JuliaKeywords::instance()->variables());
+ addFunctions(JuliaKeywords::instance()->functions());
}
void JuliaHighlighter::highlightBlock(const QString &text)
@@ -145,7 +143,16 @@
void JuliaHighlighter::updateHighlight()
{
+ for (const auto &var : JuliaKeywords::instance()->removedVariables()) {
+ removeRule(var);
+ }
+
+ for (const auto &func : JuliaKeywords::instance()->removedFunctions()) {
+ removeRule(func);
+ }
+
addVariables(JuliaKeywords::instance()->variables());
+ addFunctions(JuliaKeywords::instance()->functions());
rehighlight();
}
diff --git a/src/backends/julia/juliakeywords.h b/src/backends/julia/juliakeywords.h
--- a/src/backends/julia/juliakeywords.h
+++ b/src/backends/julia/juliakeywords.h
@@ -27,11 +27,23 @@
static JuliaKeywords *instance();
const QStringList &keywords() const { return m_keywords; }
+
const QStringList &variables() const { return m_variables; }
+ const QStringList &removedVariables() const { return m_removedVariables; }
+ void clearVariables();
+ void addVariable(const QString &variable);
+
+ const QStringList &functions() const { return m_functions; }
+ const QStringList &removedFunctions() const { return m_removedFunctions; }
+ void clearFunctions();
+ void addFunction(const QString &function);
private:
QStringList m_keywords;
QStringList m_variables;
+ QStringList m_removedVariables;
+ QStringList m_functions;
+ QStringList m_removedFunctions;
JuliaKeywords() {}
~JuliaKeywords() {}
diff --git a/src/backends/julia/juliakeywords.cpp b/src/backends/julia/juliakeywords.cpp
--- a/src/backends/julia/juliakeywords.cpp
+++ b/src/backends/julia/juliakeywords.cpp
@@ -59,7 +59,7 @@
or name == QLatin1String("variables")) {
while (xml.readNextStartElement()) {
Q_ASSERT(
- xml.isStartElement() && xml.name() == QLatin1String("word")
+ xml.isStartElement() and xml.name() == QLatin1String("word")
);
const QString text = xml.readElementText();
@@ -79,3 +79,29 @@
qWarning() << "Error parsing keywords.xml:" << xml.errorString();
}
}
+
+void JuliaKeywords::addVariable(const QString &variable)
+{
+ if (not m_variables.contains(variable)) {
+ m_variables << variable;
+ }
+}
+
+void JuliaKeywords::clearVariables()
+{
+ m_removedVariables = m_variables;
+ m_variables.clear();
+}
+
+void JuliaKeywords::addFunction(const QString &function)
+{
+ if (not m_functions.contains(function)) {
+ m_functions << function;
+ }
+}
+
+void JuliaKeywords::clearFunctions()
+{
+ m_removedFunctions == m_functions;
+ m_functions.clear();
+}
diff --git a/src/backends/julia/juliaserver/juliaserver.cpp b/src/backends/julia/juliaserver/juliaserver.cpp
--- a/src/backends/julia/juliaserver/juliaserver.cpp
+++ b/src/backends/julia/juliaserver/juliaserver.cpp
@@ -50,8 +50,8 @@
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("const __originalSTDOUT__ = STDOUT");
+ jl_eval_string("const __originalSTDERR__ = STDERR");
jl_eval_string(
QString::fromLatin1("redirect_stdout(open(\"%1\", \"w\"))")
.arg(output.fileName()).toLatin1().constData()
@@ -92,8 +92,18 @@
}
jl_eval_string("flush(STDOUT)");
jl_eval_string("flush(STDERR)");
- jl_eval_string("redirect_stdout(originalSTDOUT)");
- jl_eval_string("redirect_stderr(originalSTDERR)");
+ jl_eval_string("redirect_stdout(__originalSTDOUT__)");
+ jl_eval_string("redirect_stderr(__originalSTDERR__)");
+
+ 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());
diff --git a/src/backends/julia/juliasession.h b/src/backends/julia/juliasession.h
--- a/src/backends/julia/juliasession.h
+++ b/src/backends/julia/juliasession.h
@@ -19,11 +19,16 @@
*/
#pragma once
+#include
+
#include "session.h"
class JuliaExpression;
class KProcess;
class QDBusInterface;
+namespace Cantor {
+ class DefaultVariableModel;
+}
class JuliaSession: public Cantor::Session
{
@@ -46,6 +51,10 @@
int index = -1) override;
virtual QSyntaxHighlighter *syntaxHighlighter(QObject *parent);
+ virtual QAbstractItemModel *variableModel() override;
+
+Q_SIGNALS:
+ void updateHighlighter();
private Q_SLOTS:
void onResultReady();
@@ -57,6 +66,10 @@
QList m_runningExpressions;
JuliaExpression *m_currentExpression;
+ Cantor::DefaultVariableModel *m_variableModel;
+
+ QMap m_whos_cache;
+
friend JuliaExpression;
void runExpression(JuliaExpression *expression);
@@ -68,4 +81,6 @@
QString getOutput();
QString getError();
bool getWasException();
+
+ void listVariables();
};
diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp
--- a/src/backends/julia/juliasession.cpp
+++ b/src/backends/julia/juliasession.cpp
@@ -25,15 +25,21 @@
#include
#include
+#include "defaultvariablemodel.h"
+
#include "juliaexpression.h"
#include "settings.h"
#include "juliahighlighter.h"
+#include "juliakeywords.h"
+#include "juliaextensions.h"
+#include "juliabackend.h"
JuliaSession::JuliaSession(Cantor::Backend *backend)
: Session(backend)
, m_process(nullptr)
, m_interface(nullptr)
, m_currentExpression(nullptr)
+ , m_variableModel(new Cantor::DefaultVariableModel(this))
{
}
@@ -89,12 +95,18 @@
JuliaSettings::self()->replPath().path()
);
+ listVariables();
+
emit ready();
}
void JuliaSession::logout()
{
m_process->terminate();
+
+ JuliaKeywords::instance()->clearVariables();
+ JuliaKeywords::instance()->clearFunctions();
+
changeStatus(Cantor::Session::Done);
}
@@ -136,9 +148,12 @@
return nullptr;
}
-QSyntaxHighlighter *JuliaSession::syntaxHighlighter(QObject* parent)
+QSyntaxHighlighter *JuliaSession::syntaxHighlighter(QObject *parent)
{
JuliaHighlighter *highlighter = new JuliaHighlighter(parent);
+ QObject::connect(
+ this, SIGNAL(updateHighlighter()), highlighter, SLOT(updateHighlight())
+ );
return highlighter;
}
@@ -162,6 +177,8 @@
m_currentExpression->finalize();
m_runningExpressions.removeAll(m_currentExpression);
+ listVariables();
+
changeStatus(Cantor::Session::Done);
}
@@ -195,4 +212,115 @@
return reply.isValid() and reply.value();
}
+void JuliaSession::listVariables()
+{
+ QStringList ignoredVariables;
+ ignoredVariables // These are tech variables of juliaserver
+ << QLatin1String("__originalSTDOUT__")
+ << QLatin1String("__originalSTDERR__");
+
+ auto rem_marker = QString::fromLatin1("\"%1\"")
+ .arg(JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER);
+
+ JuliaKeywords::instance()->clearVariables();
+ JuliaKeywords::instance()->clearFunctions();
+
+ QStringList processed_modules;
+ QStringList modules_to_process;
+ modules_to_process << QLatin1String("__GLOBAL__");
+
+ while (modules_to_process.size() > 0) {
+ auto module = modules_to_process.front();
+ modules_to_process.pop_front();
+ if (processed_modules.contains(module)) {
+ continue;
+ }
+ processed_modules << module;
+
+ 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();
+ }
+ }
+
+ 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()) {
+ continue;
+ }
+
+ QString type =
+ line.simplified().split(QLatin1String(" ")).last().simplified();
+
+ if (ignoredVariables.contains(name)) {
+ continue;
+ }
+
+ if (type == QLatin1String("Module")) {
+ modules_to_process.append(name);
+ continue;
+ }
+
+ if (type == QLatin1String("Function")) {
+ JuliaKeywords::instance()->addFunction(name);
+ continue;
+ }
+
+ if (module != QLatin1String("__GLOBAL__")) {
+ continue; // Don't add variables not included on global scope
+ }
+
+ batchCommands << QString::fromLatin1("show(%1);").arg(name);
+ batchTypes << type;
+ batchNames << name;
+ }
+
+ if (batchCommands.isEmpty()) {
+ continue;
+ }
+
+ 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) {
+ m_variableModel->removeVariable(name);
+ continue;
+ }
+ }
+
+ m_variableModel->addVariable(name, value);
+ JuliaKeywords::instance()->addVariable(name);
+ }
+ }
+
+ emit updateHighlighter();
+}
+
+QAbstractItemModel *JuliaSession::variableModel()
+{
+ return m_variableModel;
+}
+
#include "juliasession.moc"
diff --git a/src/backends/julia/juliautils.h b/src/backends/julia/juliautils.h
new file mode 100644
--- /dev/null
+++ b/src/backends/julia/juliautils.h
@@ -0,0 +1,29 @@
+/*
+ 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
+ */
+#pragma once
+
+#include
+
+inline QString fromSource(const QString &resourceName)
+{
+ QFile text(resourceName);
+ text.open(QIODevice::ReadOnly);
+ return QString::fromLatin1(text.readAll());
+}
diff --git a/src/backends/julia/variables_cleaner.jl b/src/backends/julia/variables_cleaner.jl
new file mode 100644
--- /dev/null
+++ b/src/backends/julia/variables_cleaner.jl
@@ -0,0 +1,11 @@
+# Variable cleaning script
+for name in names(Main)[4:end]
+ if name == :__originalSTDOUT__ || name == :__originalSTDERR__
+ continue
+ end
+ var_info = summary(eval(name))
+ if var_info == "Function" || var_info == "Module"
+ continue
+ end
+ @eval (($name) = "%1")
+end
diff --git a/src/backends/julia/variables_loader.jl b/src/backends/julia/variables_loader.jl
new file mode 100644
--- /dev/null
+++ b/src/backends/julia/variables_loader.jl
@@ -0,0 +1,6 @@
+# Variable loading script
+import JLD
+for (var_name, value) in JLD.load("%1")
+ s = symbol(var_name)
+ @eval (($s) = ($value))
+end
diff --git a/src/backends/julia/variables_saver.jl b/src/backends/julia/variables_saver.jl
new file mode 100644
--- /dev/null
+++ b/src/backends/julia/variables_saver.jl
@@ -0,0 +1,14 @@
+# Variable saving script
+import JLD
+JLD.jldopen("%1", "w") do file
+ for name in names(Main)[4:end]
+ if name == :__originalSTDOUT__ || name == :__originalSTDERR__
+ continue
+ end
+ var_info = summary(eval(name))
+ if var_info == "Function" || var_info == "Module"
+ continue
+ end
+ JLD.write(file, repr(name)[2:end], eval(name))
+ end
+end