summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Lakhtanov <ivan.lakhtanov@gmail.com>2016-08-20 21:44:40 (GMT)
committerIvan Lakhtanov <ivan.lakhtanov@gmail.com>2016-08-24 18:44:30 (GMT)
commitdf70016a0e0210824785499eae5422a36a8a8e32 (patch)
tree60a4db8839f856c0f93367f79f1ba4c1d5903a46
parent97277343f389fd76ccd5e0cf35e87f2d09957aa8 (diff)
Variable management
* Tracking of variables: adding, clearing, loading, saving * Syntax highlighter uses extracted functions and variables * Caching of modules variables Differential Revision: https://phabricator.kde.org/D2198
-rw-r--r--src/backends/julia/CMakeLists.txt6
-rw-r--r--src/backends/julia/juliabackend.cpp6
-rw-r--r--src/backends/julia/juliaextensions.cpp72
-rw-r--r--src/backends/julia/juliaextensions.h47
-rw-r--r--src/backends/julia/juliahighlighter.cpp13
-rw-r--r--src/backends/julia/juliakeywords.cpp26
-rw-r--r--src/backends/julia/juliakeywords.h12
-rw-r--r--src/backends/julia/juliascriptloading.h37
-rw-r--r--src/backends/julia/juliaserver/juliaserver.cpp18
-rw-r--r--src/backends/julia/juliasession.cpp128
-rw-r--r--src/backends/julia/juliasession.h15
-rw-r--r--src/backends/julia/scripts/variables_cleaner.jl11
-rw-r--r--src/backends/julia/scripts/variables_loader.jl6
-rw-r--r--src/backends/julia/scripts/variables_saver.jl14
14 files changed, 403 insertions, 8 deletions
diff --git a/src/backends/julia/CMakeLists.txt b/src/backends/julia/CMakeLists.txt
index 760a20a..f90fb9f 100644
--- a/src/backends/julia/CMakeLists.txt
+++ b/src/backends/julia/CMakeLists.txt
@@ -7,6 +7,7 @@ set(JuliaBackend_SRCS
juliaexpression.cpp
juliakeywords.cpp
juliahighlighter.cpp
+ juliaextensions.cpp
)
kconfig_add_kcfg_files(JuliaBackend_SRCS settings.kcfgc)
@@ -18,3 +19,8 @@ target_link_libraries(cantor_juliabackend Qt5::DBus)
install(FILES juliabackend.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
install(FILES keywords.xml DESTINATION ${KDE_INSTALL_DATADIR}/cantor/juliabackend)
+file(GLOB scripts "${CMAKE_CURRENT_SOURCE_DIR}/scripts/*.jl")
+install(
+ FILES ${scripts}
+ DESTINATION ${KDE_INSTALL_DATADIR}/cantor/juliabackend/scripts
+)
diff --git a/src/backends/julia/juliabackend.cpp b/src/backends/julia/juliabackend.cpp
index a0eff9d..717d2b8 100644
--- 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<QVariant> &args)
: Cantor::Backend(parent, args)
{
setEnabled(true);
+
+ new JuliaVariableManagementExtension(this);
}
QString JuliaBackend::id() const
@@ -43,7 +46,8 @@ Cantor::Session *JuliaBackend::createSession()
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.cpp b/src/backends/julia/juliaextensions.cpp
new file mode 100644
index 0000000..739cb18
--- /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 <ivan.lakhtanov@gmail.com>
+ */
+#include "juliaextensions.h"
+
+#include <QDebug>
+#include <KLocalizedString>
+
+#include "juliascriptloading.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 loadScript(QLatin1String("variables_cleaner"))
+ .arg(REMOVED_VARIABLE_MARKER);
+}
+
+QString JuliaVariableManagementExtension::saveVariables(const QString &fileName)
+{
+ return loadScript(QLatin1String("variables_saver")).arg(fileName);
+}
+
+QString JuliaVariableManagementExtension::loadVariables(const QString &fileName)
+{
+ return loadScript(QLatin1String("variables_loader")).arg(fileName);
+}
diff --git a/src/backends/julia/juliaextensions.h b/src/backends/julia/juliaextensions.h
new file mode 100644
index 0000000..080b113
--- /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 <ivan.lakhtanov@gmail.com>
+ */
+#pragma once
+
+#include <extension.h>
+
+#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/juliahighlighter.cpp b/src/backends/julia/juliahighlighter.cpp
index 65b8aaa..26a7ec7 100644
--- 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::highlightBlock(const QString &text)
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.cpp b/src/backends/julia/juliakeywords.cpp
index e15dbca..2178705 100644
--- a/src/backends/julia/juliakeywords.cpp
+++ b/src/backends/julia/juliakeywords.cpp
@@ -84,3 +84,29 @@ void JuliaKeywords::loadFromFile()
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/juliakeywords.h b/src/backends/julia/juliakeywords.h
index 74f14b7..0643e40 100644
--- a/src/backends/julia/juliakeywords.h
+++ b/src/backends/julia/juliakeywords.h
@@ -27,11 +27,23 @@ public:
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/juliascriptloading.h b/src/backends/julia/juliascriptloading.h
new file mode 100644
index 0000000..803c354
--- /dev/null
+++ b/src/backends/julia/juliascriptloading.h
@@ -0,0 +1,37 @@
+/*
+ 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 <QFile>
+#include <QStandardPaths>
+
+inline QString loadScript(const QString &scriptName)
+{
+ QFile text(
+ QStandardPaths::locate(
+ QStandardPaths::GenericDataLocation,
+ QString::fromLatin1(
+ "cantor/juliabackend/scripts/%1.jl"
+ ).arg(scriptName)
+ )
+ );
+ text.open(QIODevice::ReadOnly);
+ return QString::fromLatin1(text.readAll());
+}
diff --git a/src/backends/julia/juliaserver/juliaserver.cpp b/src/backends/julia/juliaserver/juliaserver.cpp
index 955d570..736bc2c 100644
--- a/src/backends/julia/juliaserver/juliaserver.cpp
+++ b/src/backends/julia/juliaserver/juliaserver.cpp
@@ -50,8 +50,8 @@ void JuliaServer::runJuliaCommand(const QString &command)
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 @@ void JuliaServer::runJuliaCommand(const QString &command)
}
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.cpp b/src/backends/julia/juliasession.cpp
index 50dfd42..07ca7de 100644
--- a/src/backends/julia/juliasession.cpp
+++ b/src/backends/julia/juliasession.cpp
@@ -25,15 +25,21 @@
#include <QDBusReply>
#include <QStandardPaths>
+#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 @@ void JuliaSession::login()
JuliaSettings::self()->replPath().path()
);
+ listVariables();
+
emit ready();
}
void JuliaSession::logout()
{
m_process->terminate();
+
+ JuliaKeywords::instance()->clearVariables();
+ JuliaKeywords::instance()->clearFunctions();
+
changeStatus(Cantor::Session::Done);
}
@@ -139,6 +151,9 @@ Cantor::CompletionObject *JuliaSession::completionFor(
QSyntaxHighlighter *JuliaSession::syntaxHighlighter(QObject *parent)
{
JuliaHighlighter *highlighter = new JuliaHighlighter(parent);
+ QObject::connect(
+ this, SIGNAL(updateHighlighter()), highlighter, SLOT(updateHighlight())
+ );
return highlighter;
}
@@ -162,6 +177,8 @@ void JuliaSession::onResultReady()
m_currentExpression->finalize();
m_runningExpressions.removeAll(m_currentExpression);
+ listVariables();
+
changeStatus(Cantor::Session::Done);
}
@@ -195,4 +212,115 @@ bool JuliaSession::getWasException()
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/juliasession.h b/src/backends/julia/juliasession.h
index dd2834d..eee89f7 100644
--- a/src/backends/julia/juliasession.h
+++ b/src/backends/julia/juliasession.h
@@ -19,11 +19,16 @@
*/
#pragma once
+#include <QMap>
+
#include "session.h"
class JuliaExpression;
class KProcess;
class QDBusInterface;
+namespace Cantor {
+ class DefaultVariableModel;
+}
class JuliaSession: public Cantor::Session
{
@@ -46,6 +51,10 @@ public:
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 @@ private:
QList<JuliaExpression *> m_runningExpressions;
JuliaExpression *m_currentExpression;
+ Cantor::DefaultVariableModel *m_variableModel;
+
+ QMap<QString, QString> m_whos_cache;
+
friend JuliaExpression;
void runExpression(JuliaExpression *expression);
@@ -68,4 +81,6 @@ private:
QString getOutput();
QString getError();
bool getWasException();
+
+ void listVariables();
};
diff --git a/src/backends/julia/scripts/variables_cleaner.jl b/src/backends/julia/scripts/variables_cleaner.jl
new file mode 100644
index 0000000..334001b
--- /dev/null
+++ b/src/backends/julia/scripts/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/scripts/variables_loader.jl b/src/backends/julia/scripts/variables_loader.jl
new file mode 100644
index 0000000..a649e06
--- /dev/null
+++ b/src/backends/julia/scripts/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/scripts/variables_saver.jl b/src/backends/julia/scripts/variables_saver.jl
new file mode 100644
index 0000000..b5762b0
--- /dev/null
+++ b/src/backends/julia/scripts/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