Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 2502a947 authored by Nikita Sirgienko's avatar Nikita Sirgienko

Huge improvments variable model in backends

Differential Revision: https://phabricator.kde.org/D19095
parents 008e809a 8a55d394
......@@ -12,6 +12,7 @@ set( RBackend_SRCS
rhighlighter.cpp
rkeywords.cpp
rsettingswidget.cpp
rvariablemodel.cpp
)
kconfig_add_kcfg_files(RBackend_SRCS rserver/settings.kcfgc)
......@@ -26,4 +27,15 @@ add_backend(rbackend ${RBackend_SRCS})
set_target_properties( cantor_rbackend PROPERTIES INSTALL_RPATH_USE_LINK_PATH false)
target_link_libraries( cantor_rbackend ${R_USED_LIBS} KF5::SyntaxHighlighting)
if(BUILD_TESTING)
add_executable( testr testr.cpp)
add_test(testr testr)
ecm_mark_as_test(testr)
target_link_libraries( testr
Qt5::Test
cantorlibs
cantortest
)
endif(BUILD_TESTING)
install( FILES cantor_r.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} )
......@@ -65,10 +65,15 @@ Cantor::Session* RBackend::createSession()
Cantor::Backend::Capabilities RBackend::capabilities() const
{
qDebug()<<"Requesting capabilities of RSession";
return Cantor::Backend::InteractiveMode |
Cantor::Backend::SyntaxHighlighting |
Cantor::Backend::Completion |
Cantor::Backend::VariableManagement;
Cantor::Backend::Capabilities cap=
SyntaxHighlighting|
Completion |
InteractiveMode;
if (RServerSettings::variableManagement())
cap |= VariableManagement;
return cap;
}
bool RBackend::requirementsFullfilled() const
......
......@@ -21,6 +21,7 @@
#include "rhighlighter.h"
#include "rkeywords.h"
#include "rsession.h"
#include <QTextEdit>
#include <QDebug>
......@@ -32,7 +33,7 @@ const QStringList RHighlighter::operators_list=QStringList()
const QStringList RHighlighter::specials_list=QStringList()
<< QLatin1String("BUG") << QLatin1String("TODO") << QLatin1String("FIXME") << QLatin1String("NB") << QLatin1String("WARNING") << QLatin1String("ERROR");
RHighlighter::RHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent)
RHighlighter::RHighlighter(QObject* parent, RSession* session) : Cantor::DefaultHighlighter(parent, session)
{
addKeywords(RKeywords::instance()->keywords());
......@@ -42,11 +43,6 @@ RHighlighter::RHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent)
specials.append(QRegExp(QLatin1String("\\b")+s+QLatin1String("\\b")));
}
void RHighlighter::refreshSyntaxRegExps()
{
emit syntaxRegExps(variables,functions);
}
// FIXME: due to lack of lookbehinds in QRegExp here we use a flag showing if we need to shift the boundary of formatting
// to make up for the accidentally matched character
void RHighlighter::formatRule(const QRegExp &p, const QTextCharFormat &fmt, const QString& text,bool shift)
......@@ -85,8 +81,3 @@ void RHighlighter::highlightBlock(const QString& text)
massFormat(variables,variableFormat(),text);
formatRule(QRegExp(QLatin1String("\"[^\"]+\"")),stringFormat(),text); // WARNING a bit redundant
}
void RHighlighter::updateHighlighting()
{
emit rulesChanged();
}
......@@ -23,24 +23,19 @@
#include "defaulthighlighter.h"
class RSession;
class RHighlighter : public Cantor::DefaultHighlighter
{
Q_OBJECT
public:
explicit RHighlighter( QObject* parent);
explicit RHighlighter( QObject* parent, RSession* session);
~RHighlighter() override = default;
protected:
void highlightBlock(const QString &text) override;
public Q_SLOTS:
void refreshSyntaxRegExps();
void updateHighlighting();
Q_SIGNALS:
void syntaxRegExps(QVector<QRegExp>&,QVector<QRegExp>&);
private:
inline void formatRule(const QRegExp &p, const QTextCharFormat &fmt, const QString& text,bool shift=false);
inline void massFormat(const QVector<QRegExp>& rules, const QTextCharFormat &fmt, const QString& text,bool shift=false);
......
......@@ -111,9 +111,6 @@ void RServer::initR()
}
qDebug()<<"done initializing";
// FIXME: other way to search symbols, see listSymbols for details
listSymbols();
}
//Code from the RInside library
......@@ -367,9 +364,6 @@ void RServer::runCommand(const QString& cmd, bool internal)
showFiles(neededFiles);
setStatus(Idle);
// FIXME: Calling this every evaluation is probably ugly
listSymbols();
}
void RServer::completeCommand(const QString& cmd)
......@@ -420,7 +414,7 @@ void RServer::completeCommand(const QString& cmd)
void RServer::listSymbols()
{
// setStatus(RServer::Busy);
setStatus(RServer::Busy);
QStringList vars, values, funcs;
int errorOccurred; // TODO: error checks
......@@ -435,7 +429,7 @@ void RServer::listSymbols()
if (Rf_isFunction(value))
funcs << name;
else
else if (RServerSettings::variableManagement())
{
int convertStatus;
SEXP valueAsString = PROTECT(R_tryEval(lang2(install("toString"),value),nullptr,&convertStatus));
......@@ -445,6 +439,8 @@ void RServer::listSymbols()
values << QString::fromUtf8(translateCharUTF8(asChar(valueAsString)));
}
}
else
vars << name;
}
UNPROTECT(1);
......@@ -463,8 +459,7 @@ void RServer::listSymbols()
UNPROTECT(1);
emit symbolList(vars, values, funcs);
// setStatus(RServer::Idle);
setStatus(Idle);
}
void RServer::setStatus(Status status)
......
......@@ -12,6 +12,10 @@
<label>Integrate Plots into the Worksheet</label>
<default>true</default>
</entry>
<entry name="variableManagement" type="Bool">
<label>Enable Variable Management</label>
<default>true</default>
</entry>
<entry name="autorunScripts" type="StringList">
<label>List of scripts to autorun at the beginning of session</label>
</entry>
......
......@@ -23,6 +23,7 @@
#include "rexpression.h"
#include "rcompletionobject.h"
#include "rhighlighter.h"
#include "rvariablemodel.h"
#include <defaultvariablemodel.h>
#include <QTimer>
......@@ -33,8 +34,11 @@
#include <signal.h>
#endif
RSession::RSession(Cantor::Backend* backend) : Session(backend), m_process(nullptr), m_rServer(nullptr), m_variableModel(new Cantor::DefaultVariableModel(this))
RSession::RSession(Cantor::Backend* backend) : Session(backend),
m_process(nullptr),
m_rServer(nullptr)
{
setVariableModel(new RVariableModel(this));
}
RSession::~RSession()
......@@ -53,6 +57,7 @@ void RSession::login()
m_process = new QProcess(this);
m_process->start(QStandardPaths::findExecutable(QLatin1String("cantor_rserver")));
m_process->waitForStarted();
m_process->waitForReadyRead();
qDebug()<<m_process->readAllStandardOutput();
......@@ -60,7 +65,6 @@ void RSession::login()
m_rServer = new org::kde::Cantor::R(QString::fromLatin1("org.kde.Cantor.R-%1").arg(m_process->pid()), QLatin1String("/"), QDBusConnection::sessionBus(), this);
connect(m_rServer, SIGNAL(statusChanged(int)), this, SLOT(serverChangedStatus(int)));
connect(m_rServer, SIGNAL(symbolList(QStringList,QStringList,QStringList)),this,SLOT(receiveSymbols(QStringList,QStringList,QStringList)));
changeStatus(Session::Done);
emit loginDone();
......@@ -72,9 +76,8 @@ void RSession::logout()
qDebug()<<"logout";
m_process->terminate();
m_variableModel->clearVariables();
m_variables.clear();
m_functions.clear();
variableModel()->clearVariables();
variableModel()->clearFunctions();
emit symbolsChanged();
changeStatus(Status::Disable);
......@@ -126,39 +129,7 @@ Cantor::CompletionObject* RSession::completionFor(const QString& command, int in
QSyntaxHighlighter* RSession::syntaxHighlighter(QObject* parent)
{
RHighlighter *h=new RHighlighter(parent);
connect(h,SIGNAL(syntaxRegExps(QVector<QRegExp>&,QVector<QRegExp>&)),this,SLOT(fillSyntaxRegExps(QVector<QRegExp>&,QVector<QRegExp>&)));
connect(this,SIGNAL(symbolsChanged()),h,SLOT(refreshSyntaxRegExps()));
connect(this,SIGNAL(syntaxRegExpsFilled()), h, SLOT(updateHighlighting()));
return h;
}
void RSession::fillSyntaxRegExps(QVector<QRegExp>& v, QVector<QRegExp>& f)
{
// WARNING: current implementation as-in-maxima is a performance hit
// think about grouping expressions together or only fetching needed ones
v.clear(); f.clear();
foreach (const QString s, m_variables)
if (!s.contains(QRegExp(QLatin1String("[^A-Za-z0-9_.]"))))
v.append(QRegExp(QLatin1String("\\b")+s+QLatin1String("\\b")));
foreach (const QString s, m_functions)
if (!s.contains(QRegExp(QLatin1String("[^A-Za-z0-9_.]"))))
f.append(QRegExp(QLatin1String("\\b")+s+QLatin1String("\\b")));
emit syntaxRegExpsFilled();
}
void RSession::receiveSymbols(const QStringList& vars, const QStringList& values, const QStringList & funcs)
{
m_variables = vars;
for (int i = 0; i < vars.count(); i++)
{
m_variableModel->addVariable(vars[i], values[i]);
}
m_functions = funcs;
emit symbolsChanged();
return new RHighlighter(parent, this);
}
void RSession::serverChangedStatus(int status)
......@@ -168,14 +139,10 @@ void RSession::serverChangedStatus(int status)
{
if(!expressionQueue().isEmpty())
{
RExpression* expr = static_cast<RExpression*>(expressionQueue().takeFirst());
Cantor::Expression* expr = expressionQueue().first();
qDebug()<<"done running "<<expr<<" "<<expr->command();
}
if(expressionQueue().isEmpty())
changeStatus(Cantor::Session::Done);
else
runFirstExpression();
finishFirstExpression();
}
else
changeStatus(Cantor::Session::Running);
......@@ -209,7 +176,9 @@ void RSession::sendInputToServer(const QString& input)
m_rServer->answerRequest(s);
}
QAbstractItemModel* RSession::variableModel()
void RSession::updateSymbols(const RVariableModel* model)
{
return m_variableModel;
disconnect(m_rServer, SIGNAL(symbolList(QStringList,QStringList,QStringList)));
connect(m_rServer, SIGNAL(symbolList(QStringList,QStringList,QStringList)), model, SLOT(parseResult(QStringList,QStringList,QStringList)));
m_rServer->listSymbols();
}
......@@ -28,6 +28,7 @@
#include "rserver_interface.h"
class RExpression;
class RVariableModel;
class QProcess;
namespace Cantor {
......@@ -49,27 +50,19 @@ class RSession : public Cantor::Session
Cantor::Expression* evaluateExpression(const QString& command, Cantor::Expression::FinishingBehavior behave = Cantor::Expression::FinishingBehavior::DoNotDelete, bool internal = false) override;
Cantor::CompletionObject* completionFor(const QString& command, int index=-1) override;
QSyntaxHighlighter* syntaxHighlighter(QObject* parent) override;
QAbstractItemModel* variableModel() override;
void sendInputToServer(const QString& input);
void runFirstExpression() override;
void sendInputToServer(const QString& input);
void updateSymbols(const RVariableModel* model);
protected Q_SLOTS:
void serverChangedStatus(int status);
void receiveSymbols(const QStringList& vars, const QStringList& values, const QStringList & funcs);
void fillSyntaxRegExps(QVector<QRegExp>& v, QVector<QRegExp>& f);
Q_SIGNALS:
void symbolsChanged();
void syntaxRegExpsFilled();
private:
QProcess* m_process;
org::kde::Cantor::R* m_rServer;
/* Available variables and functions, TODO make full classes and type info */
Cantor::DefaultVariableModel* m_variableModel;
QStringList m_variables;
QStringList m_functions;
};
#endif /* _RSESSION_H */
/*
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) 2018 Nikita Sirgienko <warquark@gmail.com>
*/
#include "rvariablemodel.h"
#include "rsession.h"
using namespace Cantor;
RVariableModel::RVariableModel(RSession* session):
DefaultVariableModel(session)
{
}
void RVariableModel::update()
{
static_cast<RSession*>(session())->updateSymbols(this);
}
void RVariableModel::parseResult(const QStringList& names, const QStringList& values, const QStringList& funcs)
{
QList<Variable> vars;
if (!values.isEmpty()) // Variables management disabled
for (int i = 0; i < names.size(); i++)
vars.append(Variable{names[i], values[i]});
else
for (int i = 0; i < names.size(); i++)
vars.append(Variable{names[i], QString()});
setVariables(vars);
setFunctions(funcs);
}
/*
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) 2018 Nikita Sirgienko <warquark@gmail.com>
*/
#ifndef _RVARIABLEMODEL_H
#define _RVARIABLEMODEL_H
#include "defaultvariablemodel.h"
class RSession;
class RVariableModel : public Cantor::DefaultVariableModel
{
Q_OBJECT
public:
RVariableModel( RSession* session);
~RVariableModel() override = default;
void update() override;
public Q_SLOTS:
void parseResult(const QStringList& names, const QStringList& values, const QStringList& funcs);
private:
QStringList m_functions;
};
#endif /* _RVARIABLEMODEL_H */
......@@ -32,6 +32,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="kcfg_variableManagement">
<property name="toolTip">
<string>Let Cantor follow the creation/destruction of variables</string>
</property>
<property name="text">
<string>Enable Variable Management</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox">
<property name="title">
......
/*
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) 2019 Sirgienko Nikita <warquark@gmail.com>
*/
#include "testr.h"
#include "session.h"
#include "backend.h"
#include "expression.h"
#include "result.h"
#include "completionobject.h"
#include "defaultvariablemodel.h"
#include <QDebug>
QString TestR::backendName()
{
return QLatin1String("R");
}
void TestR::testVariablesCreatingFromCode()
{
QAbstractItemModel* model = session()->variableModel();
QVERIFY(model != nullptr);
Cantor::Expression* e=evalExp(QLatin1String("a1 = 15; b1 = 'S'; d1 = c(1,2,3)"));
QVERIFY(e!=nullptr);
while(session()->status() != Cantor::Session::Done)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(3, model->rowCount());
QCOMPARE(model->index(0,0).data().toString(), QLatin1String("a1"));
QCOMPARE(model->index(0,1).data().toString(), QLatin1String("15"));
QCOMPARE(model->index(1,0).data().toString(), QLatin1String("b1"));
QCOMPARE(model->index(1,1).data().toString(), QLatin1String("S"));
QCOMPARE(model->index(2,0).data().toString(), QLatin1String("d1"));
QCOMPARE(model->index(2,1).data().toString(), QLatin1String("1, 2, 3"));
evalExp(QLatin1String("rm(a1,b1,d1)"));
}
void TestR::testVariableCleanupAfterRestart()
{
Cantor::DefaultVariableModel* model = session()->variableModel();
QVERIFY(model != nullptr);
while(session()->status() != Cantor::Session::Done)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(static_cast<QAbstractItemModel*>(model)->rowCount(), 0);
Cantor::Expression* e=evalExp(QLatin1String("h1 = 15; h2 = 'S';"));
QVERIFY(e!=nullptr);
while(session()->status() != Cantor::Session::Done)
waitForSignal(session(), SIGNAL(statusChanged(Cantor::Session::Status)));
QCOMPARE(static_cast<QAbstractItemModel*>(model)->rowCount(), 2);
session()->logout();
session()->login();
QCOMPARE(static_cast<QAbstractItemModel*>(model)->rowCount(), 0);
}
QTEST_MAIN( TestR )
/*
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) 2019 Sirgienko Nikita <warquark@gmail.com>
*/
#ifndef _TESTR_H
#define _TESTR_H
#include "backendtest.h"
class TestR : public BackendTest
{
Q_OBJECT
private Q_SLOTS:
//tests variable model
void testVariablesCreatingFromCode();
void testVariableCleanupAfterRestart();
private:
QString backendName() override;
};
#endif /* _TESTR_H */
......@@ -7,6 +7,7 @@ set(JuliaBackend_SRCS
juliasession.cpp
juliaexpression.cpp
juliakeywords.cpp
juliavariablemodel.cpp
juliahighlighter.cpp
juliaextensions.cpp
juliacompletionobject.cpp
......@@ -26,9 +27,9 @@ target_link_libraries(cantor_juliabackend
if(BUILD_TESTING)
add_executable(testjulia testjulia.cpp)
add_test(NAME testjulia COMMAND testjulia)
target_link_libraries(testjulia
target_link_libraries(testjulia
Qt5::Test
cantorlibs
cantorlibs
cantortest
)
endif(BUILD_TESTING)
......
......@@ -55,9 +55,14 @@ Cantor::Session *JuliaBackend::createSession()
Cantor::Backend::Capabilities JuliaBackend::capabilities() const
{
return Cantor::Backend::SyntaxHighlighting |
Cantor::Backend::VariableManagement |
Cantor::Backend::Completion;
Cantor::Backend::Capabilities cap=
SyntaxHighlighting|
Completion;
if (JuliaSettings::variableManagement())
cap |= VariableManagement;
return cap;
}
QString JuliaBackend::description() const
......
......@@ -12,6 +12,10 @@
QUrl::fromLocalFile(QStandardPaths::findExecutable(QLatin1String("julia")))
</default>
</entry>
<entry name="variableManagement" type="Bool">
<label>Enable Variable Management</label>
<default>true</default>
</entry>
<entry name="integratePlots" type="Bool">
<label>Integrate Plots into the Worksheet</label>
<default>true</default>
......
......@@ -20,13 +20,14 @@
#include "juliahighlighter.h"
#include "juliakeywords.h"
#include "juliasession.h"
#include <climits>
#include <QTextEdit>
#include <QDebug>
JuliaHighlighter::JuliaHighlighter(QObject *parent)
: Cantor::DefaultHighlighter(parent)
JuliaHighlighter::JuliaHighlighter(QObject *parent, JuliaSession* session)
: Cantor::DefaultHighlighter(parent, session)
{
addKeywords(JuliaKeywords::instance()->keywords());
addVariables(JuliaKeywords::instance()->variables());
......@@ -163,22 +164,6 @@ void JuliaHighlighter::highlightBlock(const QString &text)
setCurrentBlockState(state);
}
void JuliaHighlighter::updateHighlight()
{
// Remove rules for outdated variables and functions
for (const auto &var : JuliaKeywords::instance()->removedVariables()) {
removeRule(var);
}
for (const auto &func : JuliaKeywords::instance()->removedFunctions()) {
removeRule(func);
}
// Add actual variables and function