diff --git a/src/backends/R/rcompletionobject.cpp b/src/backends/R/rcompletionobject.cpp index 104f7738..7b092b4a 100644 --- a/src/backends/R/rcompletionobject.cpp +++ b/src/backends/R/rcompletionobject.cpp @@ -1,72 +1,110 @@ /* 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) 2010 Oleksiy Protas */ #include "rcompletionobject.h" #include "rkeywords.h" #include "rsession.h" +#include "result.h" -RCompletionObject::RCompletionObject(const QString& command, int index, RSession* session) : Cantor::CompletionObject(session) +using namespace Cantor; + +RCompletionObject::RCompletionObject(const QString& command, int index, RSession* session): + CompletionObject(session), + m_expression(nullptr) { setLine(command, index); } RCompletionObject::~RCompletionObject() { - // emit destroyed(this); + if (m_expression) + m_expression->setFinishingBehavior(Expression::FinishingBehavior::DeleteOnFinish); } void RCompletionObject::fetchCompletions() { - if (session()->status() == Cantor::Session::Disable) + if (session()->status() == Session::Disable) { QStringList allCompletions; allCompletions << RKeywords::instance()->keywords(); setCompletions(allCompletions); emit fetchingDone(); } else - emit requestCompletion(command()); + { + if (m_expression) + return; + + const QString cmd = QLatin1String("%completion ")+command(); + m_expression = session()->evaluateExpression(cmd, Expression::FinishingBehavior::DoNotDelete, true); + connect(m_expression, &Expression::statusChanged, this, &RCompletionObject::receiveCompletions); + } } -void RCompletionObject::receiveCompletions(const QString& token,const QStringList& options) +void RCompletionObject::receiveCompletions(Cantor::Expression::Status status) { - /* Setting up both completion variants -and- the token R generously found for us */ - //setCommand(token); - //setCompletions(options); - - // TODO: investigate the empty token problem - /* Not so fast, evidently KCompletion requires a nonempty token, hence this stub */ - if (token.length()==0 && command().length()!=0) - { - /* Adding previous symbol to token, ugly but effective */ - QString lastchar(command().at(command().length()-1)); - setCommand(lastchar); - setCompletions(QStringList(options).replaceInStrings(QRegExp(QLatin1String("^")), lastchar)); - } - else + switch(status) { - setCommand(token); - setCompletions(options); + case Expression::Status::Done: + { + if (!m_expression->result()) + break; + + const QChar recordSep(30); + const QChar unitSep(31); + + const QString output = m_expression->result()->data().toString(); + + const QString& token = output.section(unitSep, 0, 0); + const QStringList& options = output.section(unitSep, 1, 1).split(recordSep, QString::SkipEmptyParts); + + // TODO: investigate the empty token problem + /* Not so fast, evidently KCompletion requires a nonempty token, hence this stub */ + if (token.length()==0 && command().length()!=0) + { + /* Adding previous symbol to token, ugly but effective */ + QString lastchar(command().at(command().length()-1)); + setCommand(lastchar); + setCompletions(QStringList(options).replaceInStrings(QRegExp(QLatin1String("^")), lastchar)); + } + else + { + setCommand(token); + setCompletions(options); + } + + emit fetchingDone(); + } + case Expression::Status::Error: + qWarning() << "R code for completion command finishs with error message: " << m_expression->errorMessage(); + break; + + case Expression::Status::Interrupted: + break; + + default: + return; } - emit fetchingDone(); + m_expression->deleteLater(); + m_expression = nullptr; } diff --git a/src/backends/R/rcompletionobject.h b/src/backends/R/rcompletionobject.h index f55b2e6b..6553732d 100644 --- a/src/backends/R/rcompletionobject.h +++ b/src/backends/R/rcompletionobject.h @@ -1,43 +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) 2010 Oleksiy Protas */ #ifndef _RCOMPLETIONOBJECT_H #define _RCOMPLETIONOBJECT_H #include "completionobject.h" +#include class RSession; class RCompletionObject : public Cantor::CompletionObject { Q_OBJECT public: RCompletionObject( const QString& cmd, int index, RSession* session ); ~RCompletionObject() override; protected Q_SLOTS: void fetchCompletions() override; - void receiveCompletions(const QString& token,const QStringList& options); + void receiveCompletions(Cantor::Expression::Status status); - Q_SIGNALS: - void requestCompletion(const QString&); + private: + Cantor::Expression* m_expression; }; #endif /* _RCOMPLETIONOBJECT_H */ diff --git a/src/backends/R/rserver/org.kde.Cantor.R.xml b/src/backends/R/rserver/org.kde.Cantor.R.xml index 1cf4f49c..f31de1cb 100644 --- a/src/backends/R/rserver/org.kde.Cantor.R.xml +++ b/src/backends/R/rserver/org.kde.Cantor.R.xml @@ -1,40 +1,27 @@ - - - - - - - - - - - - + - - diff --git a/src/backends/R/rserver/rserver.cpp b/src/backends/R/rserver/rserver.cpp index 1d910e33..ce831fe3 100644 --- a/src/backends/R/rserver/rserver.cpp +++ b/src/backends/R/rserver/rserver.cpp @@ -1,511 +1,536 @@ /* 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) 2009 Alexander Rieder Copyright (C) 2010 Oleksiy Protas */ // TODO: setStatus in syntax and completions, to be or not to be? // on the one hand comme il faut, on another, causes flickering in UI #include "rserver.h" #include #include "radaptor.h" #include "rcallbacks.h" #include "settings.h" #include #include #include #include #include //R includes #include #include #include #include #define R_INTERFACE_PTRS #include +const QChar RServer::recordSep(30); +const QChar RServer::unitSep(31); + RServer::RServer() : m_isInitialized(false),m_isCompletionAvailable(false) { new RAdaptor(this); m_tmpDir = QDir::tempPath() + QString::fromLatin1("/cantor_rserver-%1").arg(getpid()); QDir dir; dir.mkdir(m_tmpDir); qDebug()<<"RServer: "<<"storing plots at "<integratePlots()) { qDebug()<<"RServer: "<<"integrating plots"; newPlotDevice(); } //Loading automatic run scripts foreach (const QString& path, RServerSettings::self()->autorunScripts()) { int errorOccurred=0; if (QFile::exists(path)) R_tryEval(lang2(install("source"),mkString(path.toUtf8().data())),nullptr,&errorOccurred); // TODO: error handling else { qDebug()<<"RServer: "<<(QLatin1String("Script ")+path+QLatin1String(" not found")); // FIXME: or should we throw a messagebox } } qDebug()<<"RServer: "<<"done initializing"; } //Code from the RInside library void RServer::autoload() { #include "rautoloads.h" /* Autoload default packages and names from autoloads.h * * This function behaves in almost every way like * R's autoload: * function (name, package, reset = FALSE, ...) * { * if (!reset && exists(name, envir = .GlobalEnv, inherits = FALSE)) * stop("an object with that name already exists") * m <- match.call() * m[[1]] <- as.name("list") * newcall <- eval(m, parent.frame()) * newcall <- as.call(c(as.name("autoloader"), newcall)) * newcall$reset <- NULL * if (is.na(match(package, .Autoloaded))) * assign(".Autoloaded", c(package, .Autoloaded), env = .AutoloadEnv) * do.call("delayedAssign", list(name, newcall, .GlobalEnv, * .AutoloadEnv)) * invisible() * } * * What's missing is the updating of the string vector .Autoloaded with * the list of packages, which by my code analysis is useless and only * for informational purposes. * */ //void autoloads(void){ SEXP da, dacall, al, alcall, AutoloadEnv, name, package; int i,j, idx=0, errorOccurred, ptct; /* delayedAssign call*/ PROTECT(da = Rf_findFun(Rf_install("delayedAssign"), R_GlobalEnv)); PROTECT(AutoloadEnv = Rf_findVar(Rf_install(".AutoloadEnv"), R_GlobalEnv)); if (AutoloadEnv == R_NilValue){ qDebug()<<"RServer: "<<"Cannot find .AutoloadEnv"; //exit(1); } PROTECT(dacall = allocVector(LANGSXP,5)); SETCAR(dacall,da); /* SETCAR(CDR(dacall),name); */ /* arg1: assigned in loop */ /* SETCAR(CDR(CDR(dacall)),alcall); */ /* arg2: assigned in loop */ SETCAR(CDR(CDR(CDR(dacall))),R_GlobalEnv); /* arg3 */ SETCAR(CDR(CDR(CDR(CDR(dacall)))),AutoloadEnv); /* arg3 */ /* autoloader call */ PROTECT(al = Rf_findFun(Rf_install("autoloader"), R_GlobalEnv)); PROTECT(alcall = allocVector(LANGSXP,3)); SET_TAG(alcall, R_NilValue); /* just like do_ascall() does */ SETCAR(alcall,al); /* SETCAR(CDR(alcall),name); */ /* arg1: assigned in loop */ /* SETCAR(CDR(CDR(alcall)),package); */ /* arg2: assigned in loop */ ptct = 5; for(i = 0; i < packc; ++i){ idx += (i != 0)? packobjc[i-1] : 0; for (j = 0; j < packobjc[i]; ++j){ /*printf("autload(%s,%s)\n",packobj[idx+j],pack[i]);*/ PROTECT(name = NEW_CHARACTER(1)); PROTECT(package = NEW_CHARACTER(1)); SET_STRING_ELT(name, 0, COPY_TO_USER_STRING(packobj[idx+j])); SET_STRING_ELT(package, 0, COPY_TO_USER_STRING(pack[i])); /* Set up autoloader call */ PROTECT(alcall = allocVector(LANGSXP,3)); SET_TAG(alcall, R_NilValue); /* just like do_ascall() does */ SETCAR(alcall,al); SETCAR(CDR(alcall),name); SETCAR(CDR(CDR(alcall)),package); /* Setup delayedAssign call */ SETCAR(CDR(dacall),name); SETCAR(CDR(CDR(dacall)),alcall); R_tryEval(dacall,R_GlobalEnv,&errorOccurred); if (errorOccurred){ qDebug()<<"RServer: "<<"Error calling delayedAssign!"; //exit(1); } ptct += 3; } } UNPROTECT(ptct); /* Initialize the completion libraries if needed, adapted from sys-std.c of R */ // TODO: should we do this or init on demand? // if (completion is needed) // TODO: discuss how to pass parameter { /* First check if namespace is loaded */ if (findVarInFrame(R_NamespaceRegistry,install("utils"))==R_UnboundValue) { /* Then try to load it */ SEXP cmdSexp, cmdexpr; ParseStatus status; int i; const char *p="try(loadNamespace('rcompgen'), silent=TRUE)"; PROTECT(cmdSexp=mkString(p)); cmdexpr=PROTECT(R_ParseVector(cmdSexp,-1,&status,R_NilValue)); if(status==PARSE_OK) { for(i=0;icmd=cmd; expr->hasOtherResults=false; setStatus(RServer::Busy); setCurrentExpression(expr); expr->std_buffer.clear(); expr->err_buffer.clear(); ReturnCode returnCode=RServer::SuccessCode; QString returnText; QStringList neededFiles; //Code to evaluate an R function (taken from RInside library) ParseStatus status; SEXP cmdSexp, cmdexpr = R_NilValue; SEXP result = nullptr; int i, errorOccurred; QByteArray memBuf; memBuf.append(cmd.toUtf8()); PROTECT(cmdSexp = allocVector(STRSXP, 1)); SET_STRING_ELT(cmdSexp, 0, mkChar((char*)memBuf.data())); cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue)); switch (status) { case PARSE_OK: qDebug()<<"RServer: "<<"PARSING "< 1 */ for (i = 0; i < length(cmdexpr); ++i) { result = R_tryEval(VECTOR_ELT(cmdexpr, i), nullptr, &errorOccurred); if (errorOccurred) { qDebug()<<"RServer: "<<"Error occurred."; break; } // TODO: multiple results } memBuf.clear(); break; case PARSE_INCOMPLETE: /* need to read another line */ qDebug()<<"RServer: "<<"parse incomplete.."; break; case PARSE_NULL: qDebug()<<"RServer: "<<"ParseStatus is null: "<std_buffer<<" err: "<err_buffer; //if the command didn't print anything on its own, print the result //but only, if result exists, because comment expression don't create result //TODO: handle some known result types like lists, matrices separately // to make the output look better, by using html (tables etc.) if(result && expr->std_buffer.isEmpty()&&expr->err_buffer.isEmpty()) { qDebug()<<"RServer: "<<"printing result..."; SEXP count=PROTECT(R_tryEval(lang2(install("length"),result),nullptr,&errorOccurred)); // TODO: error checks if (*INTEGER(count)==0) qDebug()<<"RServer: " << "no result, so show nothing"; else Rf_PrintValue(result); UNPROTECT(1); } setCurrentExpression(nullptr); //is this save? if(!expr->err_buffer.isEmpty()) { returnCode=RServer::ErrorCode; returnText=expr->err_buffer; } else { returnCode=RServer::SuccessCode; returnText=expr->std_buffer; } }else { returnCode=RServer::ErrorCode; returnText=i18n("Error Parsing Command"); } if(internal) { qDebug()<<"RServer: "<<"internal result: "<hasOtherResults=true; newPlotDevice(); neededFiles< */ #ifndef _RSERVER_H #define _RSERVER_H #include +#include class Expression { public: QString cmd; int returnCode; bool hasOtherResults; QString err_buffer; QString std_buffer; }; class RServer : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.Cantor.R") public: enum Status { Idle=0, Busy }; enum ReturnCode { SuccessCode=0, ErrorCode, InterruptedCode}; RServer( ); ~RServer() override; void initR(); void autoload(); void endR(); QString requestInput(const QString& prompt); void addFileToOutput(const QString& file); Q_SIGNALS: void ready(); void statusChanged(int status); void expressionFinished(int returnCode, const QString& text, const QStringList& files); - void completionFinished(const QString& token,const QStringList& options); void inputRequested(const QString& prompt); void requestAnswered(); - void symbolList(const QStringList& variables, const QStringList& values, const QStringList& functions); public Q_SLOTS: void runCommand(const QString& cmd, bool internal=false); void answerRequest(const QString& answer); - void completeCommand(const QString& cmd); // TODO: comment properly, only takes command from start to cursor - void listSymbols(); private: void setStatus(Status status); void newPlotDevice(); + void completeCommand(const QString& cmd); // TODO: comment properly, only takes command from start to cursor + void listSymbols(); + + private: + const static QChar recordSep; + const static QChar unitSep; private: bool m_isInitialized; bool m_isCompletionAvailable; Status m_status; QString m_requestCache; QString m_tmpDir; QString m_curPlotFile; QStringList m_expressionFiles; }; #endif /* _RSERVER_H */ diff --git a/src/backends/R/rsession.cpp b/src/backends/R/rsession.cpp index 4848e771..9ab9c206 100644 --- a/src/backends/R/rsession.cpp +++ b/src/backends/R/rsession.cpp @@ -1,204 +1,196 @@ /* 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) 2009 Alexander Rieder Copyright (C) 2018 Alexander Semke */ #include "rsession.h" #include "rexpression.h" #include "rcompletionobject.h" #include "rhighlighter.h" #include "rvariablemodel.h" #include #include #include #include #ifndef Q_OS_WIN #include #endif RSession::RSession(Cantor::Backend* backend) : Session(backend), m_process(nullptr), m_rServer(nullptr) { setVariableModel(new RVariableModel(this)); } RSession::~RSession() { if (m_process) m_process->terminate(); } void RSession::login() { qDebug()<<"login"; emit loginStarted(); if(m_process) m_process->deleteLater(); m_process = new QProcess(this); m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel); m_process->start(QStandardPaths::findExecutable(QLatin1String("cantor_rserver"))); m_process->waitForStarted(); m_process->waitForReadyRead(); qDebug()<readAllStandardOutput(); 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, &org::kde::Cantor::R::statusChanged, this, &RSession::serverChangedStatus); - connect(m_rServer, &org::kde::Cantor::R::symbolList, static_cast(variableModel()), &RVariableModel::parseResult); connect(m_rServer, &org::kde::Cantor::R::expressionFinished, this, &RSession::expressionFinished); connect(m_rServer, &org::kde::Cantor::R::inputRequested, this, &RSession::inputRequested); changeStatus(Session::Done); emit loginDone(); qDebug()<<"login done"; } void RSession::logout() { qDebug()<<"logout"; m_process->terminate(); variableModel()->clearVariables(); variableModel()->clearFunctions(); emit symbolsChanged(); changeStatus(Status::Disable); } void RSession::interrupt() { if(!expressionQueue().isEmpty()) { qDebug()<<"interrupting " << expressionQueue().first()->command(); if(m_process->state() != QProcess::NotRunning) { #ifndef Q_OS_WIN const int pid=m_process->pid(); kill(pid, SIGINT); #else ; //TODO: interrupt the process on windows #endif } foreach (Cantor::Expression* expression, expressionQueue()) expression->setStatus(Cantor::Expression::Interrupted); expressionQueue().clear(); qDebug()<<"done interrupting"; } changeStatus(Cantor::Session::Done); } Cantor::Expression* RSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal) { qDebug()<<"evaluating: "<setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } Cantor::CompletionObject* RSession::completionFor(const QString& command, int index) { RCompletionObject *cmp=new RCompletionObject(command, index, this); - connect(m_rServer,SIGNAL(completionFinished(QString,QStringList)),cmp,SLOT(receiveCompletions(QString,QStringList))); - connect(cmp,SIGNAL(requestCompletion(QString)),m_rServer,SLOT(completeCommand(QString))); return cmp; } QSyntaxHighlighter* RSession::syntaxHighlighter(QObject* parent) { return new RHighlighter(parent, this); } void RSession::serverChangedStatus(int status) { qDebug()<<"changed status to "<(expressionQueue().first()); if (expr->status() == Cantor::Expression::Interrupted) return; if (!files.isEmpty()) expr->showFilesAsResult(files); if(returnCode==RExpression::SuccessCode) expr->parseOutput(text); else if (returnCode==RExpression::ErrorCode) expr->parseError(text); qDebug()<<"done running "<command(); finishFirstExpression(); } } void RSession::runFirstExpression() { if (expressionQueue().isEmpty()) return; RExpression* expr = static_cast(expressionQueue().first()); qDebug()<<"running expression: "<command(); expr->setStatus(Cantor::Expression::Computing); - m_rServer->runCommand(expr->internalCommand()); + m_rServer->runCommand(expr->internalCommand(), expr->isInternal()); changeStatus(Cantor::Session::Running); } void RSession::sendInputToServer(const QString& input) { QString s=input; if(!input.endsWith(QLatin1Char('\n'))) s+=QLatin1Char('\n'); m_rServer->answerRequest(s); } void RSession::inputRequested(QString info) { if (expressionQueue().isEmpty()) return; emit expressionQueue().first()->needsAdditionalInformation(info); } - -void RSession::updateSymbols() -{ - m_rServer->listSymbols(); -} diff --git a/src/backends/R/rsession.h b/src/backends/R/rsession.h index 6bee0231..0e9d409e 100644 --- a/src/backends/R/rsession.h +++ b/src/backends/R/rsession.h @@ -1,70 +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) 2009 Alexander Rieder */ #ifndef _RSESSION_H #define _RSESSION_H #include #include #include "session.h" #include "rserver_interface.h" class RExpression; class RVariableModel; class QProcess; namespace Cantor { class DefaultVariableModel; } class RSession : public Cantor::Session { Q_OBJECT public: explicit RSession( Cantor::Backend* backend); ~RSession() override; void login() override; void logout() override; void interrupt() override; 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; void runFirstExpression() override; void sendInputToServer(const QString& input); - void updateSymbols(); protected Q_SLOTS: void serverChangedStatus(int status); void expressionFinished(int returnCode, const QString& text, const QStringList& files); void inputRequested(QString info); Q_SIGNALS: void symbolsChanged(); private: QProcess* m_process; org::kde::Cantor::R* m_rServer; }; #endif /* _RSESSION_H */ diff --git a/src/backends/R/rvariablemodel.cpp b/src/backends/R/rvariablemodel.cpp index d888d9e0..f67d52b3 100644 --- a/src/backends/R/rvariablemodel.cpp +++ b/src/backends/R/rvariablemodel.cpp @@ -1,49 +1,93 @@ /* 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 */ #include "rvariablemodel.h" #include "rsession.h" +#include + using namespace Cantor; RVariableModel::RVariableModel(RSession* session): - DefaultVariableModel(session) + DefaultVariableModel(session), + m_expression(nullptr) +{ +} + +RVariableModel::~RVariableModel() { + if (m_expression) + m_expression->setFinishingBehavior(Expression::FinishingBehavior::DeleteOnFinish); } void RVariableModel::update() { - static_cast(session())->updateSymbols(); + if (m_expression) + return; + + m_expression = session()->evaluateExpression(QLatin1String("%model update"), Expression::FinishingBehavior::DoNotDelete, true); + connect(m_expression, &Expression::statusChanged, this, &RVariableModel::parseResult); } -void RVariableModel::parseResult(const QStringList& names, const QStringList& values, const QStringList& funcs) +void RVariableModel::parseResult(Cantor::Expression::Status status) { - QList 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()}); + switch(status) + { + case Expression::Status::Done: + { + if (!m_expression->result()) + break; + + const QChar recordSep(30); + const QChar unitSep(31); + + const QString output = m_expression->result()->data().toString(); + + const QStringList names = output.section(unitSep, 0, 0).split(recordSep, QString::SkipEmptyParts); + const QStringList values = output.section(unitSep, 1, 1).split(recordSep, QString::SkipEmptyParts); + const QStringList funcs = output.section(unitSep, 2, 2).split(recordSep, QString::SkipEmptyParts); + + QList 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); + break; + } + case Expression::Status::Error: + qWarning() << "R code for update variable model finishs with error message: " << m_expression->errorMessage(); + break; + + case Expression::Status::Interrupted: + break; - setVariables(vars); + default: + return; + } - setFunctions(funcs); + m_expression->deleteLater(); + m_expression = nullptr; } diff --git a/src/backends/R/rvariablemodel.h b/src/backends/R/rvariablemodel.h index ca5b85e7..11eb32aa 100644 --- a/src/backends/R/rvariablemodel.h +++ b/src/backends/R/rvariablemodel.h @@ -1,44 +1,45 @@ /* 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 */ #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; + ~RVariableModel() override; void update() override; public Q_SLOTS: - void parseResult(const QStringList& names, const QStringList& values, const QStringList& funcs); + void parseResult(Cantor::Expression::Status status); private: QStringList m_functions; + Cantor::Expression* m_expression; }; #endif /* _RVARIABLEMODEL_H */