File indexing completed on 2024-05-05 11:55:58

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2009 Alexander Rieder <alexanderrieder@gmail.com>
0004     SPDX-FileCopyrightText: 2018 Alexander Semke <alexander.semke@web.de>
0005 */
0006 
0007 #include "rsession.h"
0008 #include "rexpression.h"
0009 #include "rcompletionobject.h"
0010 #include "rhighlighter.h"
0011 #include "rvariablemodel.h"
0012 #include <defaultvariablemodel.h>
0013 
0014 #include <QTimer>
0015 #include <QDebug>
0016 #include <KProcess>
0017 
0018 #ifndef Q_OS_WIN
0019 #include <signal.h>
0020 #endif
0021 
0022 RSession::RSession(Cantor::Backend* backend) : Session(backend),
0023 m_process(nullptr),
0024 m_rServer(nullptr)
0025 {
0026     setVariableModel(new RVariableModel(this));
0027 }
0028 
0029 RSession::~RSession()
0030 {
0031     if (m_process)
0032         m_process->terminate();
0033 }
0034 
0035 void RSession::login()
0036 {
0037     qDebug()<<"login";
0038     if (m_process)
0039         return;
0040     emit loginStarted();
0041 
0042     m_process = new QProcess(this);
0043     m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
0044 #ifdef Q_OS_WIN
0045     m_process->start(QStandardPaths::findExecutable(QLatin1String("cantor_rserver.exe")));
0046 #else
0047     m_process->start(QStandardPaths::findExecutable(QLatin1String("cantor_rserver")));
0048 #endif
0049 
0050     m_process->waitForStarted();
0051     m_process->waitForReadyRead();
0052     qDebug()<<m_process->readAllStandardOutput();
0053 
0054     m_rServer = new org::kde::Cantor::R(QString::fromLatin1("org.kde.Cantor.R-%1").arg(m_process->processId()),  QLatin1String("/"), QDBusConnection::sessionBus(), this);
0055 
0056     connect(m_rServer, &org::kde::Cantor::R::statusChanged, this, &RSession::serverChangedStatus);
0057     connect(m_rServer,  &org::kde::Cantor::R::expressionFinished, this, &RSession::expressionFinished);
0058     connect(m_rServer, &org::kde::Cantor::R::inputRequested, this, &RSession::inputRequested);
0059 
0060     changeStatus(Session::Done);
0061     emit loginDone();
0062     qDebug()<<"login done";
0063 }
0064 
0065 void RSession::logout()
0066 {
0067     qDebug()<<"logout";
0068     if (!m_process)
0069         return;
0070 
0071     if(status() == Cantor::Session::Running)
0072         interrupt();
0073 
0074     m_process->kill();
0075     m_process->deleteLater();
0076     m_process = nullptr;
0077 
0078     Session::logout();
0079 }
0080 
0081 void RSession::interrupt()
0082 {
0083     if(!expressionQueue().isEmpty())
0084     {
0085         qDebug()<<"interrupting " << expressionQueue().first()->command();
0086         if(m_process && m_process->state() != QProcess::NotRunning)
0087         {
0088 #ifndef Q_OS_WIN
0089             const int pid = m_process->processId();
0090             kill(pid, SIGINT);
0091 #else
0092             ; //TODO: interrupt the process on windows
0093 #endif
0094         }
0095         for (auto* expression : expressionQueue())
0096             expression->setStatus(Cantor::Expression::Interrupted);
0097         expressionQueue().clear();
0098 
0099         qDebug()<<"done interrupting";
0100     }
0101 
0102     changeStatus(Cantor::Session::Done);
0103 }
0104 
0105 Cantor::Expression* RSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal)
0106 {
0107     qDebug()<<"evaluating: "<<cmd;
0108     auto* expr = new RExpression(this, internal);
0109     expr->setFinishingBehavior(behave);
0110     expr->setCommand(cmd);
0111 
0112     expr->evaluate();
0113 
0114     return expr;
0115 }
0116 
0117 Cantor::CompletionObject* RSession::completionFor(const QString& command, int index)
0118 {
0119     auto* cmp = new RCompletionObject(command, index, this);
0120     return cmp;
0121 }
0122 
0123 QSyntaxHighlighter* RSession::syntaxHighlighter(QObject* parent)
0124 {
0125     return new RHighlighter(parent, this);
0126 }
0127 
0128 void RSession::serverChangedStatus(int status)
0129 {
0130     qDebug()<<"changed status to "<<status;
0131     if(status==0)
0132     {
0133         if(expressionQueue().isEmpty())
0134             changeStatus(Cantor::Session::Done);
0135     }
0136     else
0137         changeStatus(Cantor::Session::Running);
0138 }
0139 
0140 void RSession::expressionFinished(int returnCode, const QString& text, const QStringList& files)
0141 {
0142     if (!expressionQueue().isEmpty())
0143     {
0144         auto* expr = expressionQueue().first();
0145         if (expr->status() == Cantor::Expression::Interrupted)
0146             return;
0147 
0148         static_cast<RExpression*>(expr)->showFilesAsResult(files);
0149 
0150         if(returnCode==RExpression::SuccessCode)
0151             expr->parseOutput(text);
0152         else if (returnCode==RExpression::ErrorCode)
0153             expr->parseError(text);
0154 
0155         qDebug()<<"done running "<<expr<<" "<<expr->command();
0156         finishFirstExpression();
0157     }
0158 }
0159 
0160 void RSession::runFirstExpression()
0161 {
0162     if (expressionQueue().isEmpty())
0163         return;
0164 
0165     auto* expr = expressionQueue().first();
0166     qDebug()<<"running expression: "<<expr->command();
0167 
0168     expr->setStatus(Cantor::Expression::Computing);
0169     m_rServer->runCommand(expr->internalCommand(), expr->isInternal());
0170     changeStatus(Cantor::Session::Running);
0171 }
0172 
0173 void RSession::sendInputToServer(const QString& input)
0174 {
0175     QString s = input;
0176     if(!input.endsWith(QLatin1Char('\n')))
0177         s += QLatin1Char('\n');
0178     m_rServer->answerRequest(s);
0179 }
0180 
0181 void RSession::inputRequested(QString info)
0182 {
0183     if (expressionQueue().isEmpty())
0184         return;
0185 
0186     emit expressionQueue().first()->needsAdditionalInformation(info);
0187 }