diff --git a/src/backends/R/rsession.cpp b/src/backends/R/rsession.cpp index 450c7588..7ba31630 100644 --- a/src/backends/R/rsession.cpp +++ b/src/backends/R/rsession.cpp @@ -1,212 +1,217 @@ /* 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 #include #include #include #ifndef Q_OS_WIN #include #endif RSession::RSession(Cantor::Backend* backend) : Session(backend), m_process(nullptr), m_rServer(nullptr), m_variableModel(new Cantor::DefaultVariableModel(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->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, 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(); qDebug()<<"login done"; } void RSession::logout() { qDebug()<<"logout"; m_process->terminate(); + m_variableModel->clearVariables(); + m_variables.clear(); + m_functions.clear(); + 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 } expressionQueue().first()->interrupt(); expressionQueue().removeFirst(); foreach (Cantor::Expression* expression, expressionQueue()) expression->setStatus(Cantor::Expression::Done); 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) { RHighlighter *h=new RHighlighter(parent); connect(h,SIGNAL(syntaxRegExps(QVector&,QVector&)),this,SLOT(fillSyntaxRegExps(QVector&,QVector&))); connect(this,SIGNAL(symbolsChanged()),h,SLOT(refreshSyntaxRegExps())); connect(this,SIGNAL(syntaxRegExpsFilled()), h, SLOT(updateHighlighting())); return h; } void RSession::fillSyntaxRegExps(QVector& v, QVector& 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(); } void RSession::serverChangedStatus(int status) { qDebug()<<"changed status to "<(expressionQueue().takeFirst()); qDebug()<<"done running "<command(); } if(expressionQueue().isEmpty()) changeStatus(Cantor::Session::Done); else runFirstExpression(); } else changeStatus(Cantor::Session::Running); } void RSession::runFirstExpression() { if (expressionQueue().isEmpty()) return; disconnect(m_rServer, SIGNAL(expressionFinished(int,QString)), nullptr, nullptr); disconnect(m_rServer, SIGNAL(inputRequested(QString)), nullptr, nullptr); disconnect(m_rServer, SIGNAL(showFilesNeeded(QStringList)), nullptr, nullptr); qDebug()<<"size: "<(expressionQueue().first()); qDebug()<<"running expression: "<command(); connect(m_rServer, SIGNAL(expressionFinished(int,QString)), expr, SLOT(finished(int,QString))); connect(m_rServer, SIGNAL(inputRequested(QString)), expr, SIGNAL(needsAdditionalInformation(QString))); connect(m_rServer, SIGNAL(showFilesNeeded(QStringList)), expr, SLOT(showFilesAsResult(QStringList))); expr->setStatus(Cantor::Expression::Computing); m_rServer->runCommand(expr->command()); } void RSession::sendInputToServer(const QString& input) { QString s=input; if(!input.endsWith(QLatin1Char('\n'))) s+=QLatin1Char('\n'); m_rServer->answerRequest(s); } QAbstractItemModel* RSession::variableModel() { return m_variableModel; } diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp index 0df5c8b1..13d0bbc5 100644 --- a/src/backends/julia/juliasession.cpp +++ b/src/backends/julia/juliasession.cpp @@ -1,284 +1,286 @@ /* 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 "juliasession.h" #include #include #include #include #include #include "defaultvariablemodel.h" #include "juliaexpression.h" #include "settings.h" #include "juliahighlighter.h" #include "juliakeywords.h" #include "juliaextensions.h" #include "juliabackend.h" #include "juliacompletionobject.h" #include const QRegularExpression JuliaSession::typeVariableInfo = QRegularExpression(QLatin1String("\\w+\\[")); JuliaSession::JuliaSession(Cantor::Backend *backend) : Session(backend) , m_process(nullptr) , m_interface(nullptr) , m_currentExpression(nullptr) , m_variableModel(new Cantor::DefaultVariableModel(this)) { } void JuliaSession::login() { emit loginStarted(); if (m_process) { m_process->deleteLater(); } m_process = new KProcess(this); m_process->setOutputChannelMode(KProcess::SeparateChannels); (*m_process) << QStandardPaths::findExecutable(QLatin1String("cantor_juliaserver")); m_process->start(); m_process->waitForStarted(); m_process->waitForReadyRead(); QTextStream stream(m_process->readAllStandardOutput()); QString readyStatus = QLatin1String("ready"); while (m_process->state() == QProcess::Running) { const QString &rl = stream.readLine(); if (rl == readyStatus) { break; } } if (!QDBusConnection::sessionBus().isConnected()) { qWarning() << "Can't connect to the D-Bus session bus.\n" "To start it, run: eval `dbus-launch --auto-syntax`"; return; } const QString &serviceName = QString::fromLatin1("org.kde.Cantor.Julia-%1").arg(m_process->pid()); m_interface = new QDBusInterface( serviceName, QString::fromLatin1("/"), QString(), QDBusConnection::sessionBus() ); if (!m_interface->isValid()) { qWarning() << QDBusConnection::sessionBus().lastError().message(); return; } m_interface->call( QString::fromLatin1("login"), JuliaSettings::self()->replPath().path() ); listVariables(); // Plots integration if (integratePlots()) { runJuliaCommand( QLatin1String("import GR; ENV[\"GKS_WSTYPE\"] = \"nul\"") ); } changeStatus(Session::Done); emit loginDone(); qDebug() << "login to julia " << JULIA_VERSION_STRING << "done"; } void JuliaSession::logout() { m_process->terminate(); JuliaKeywords::instance()->clearVariables(); JuliaKeywords::instance()->clearFunctions(); + m_variableModel->clearVariables(); + emit updateHighlighter(); changeStatus(Status::Disable); } void JuliaSession::interrupt() { if (m_process->pid()) { m_process->kill(); } for (Cantor::Expression *e : m_runningExpressions) { e->interrupt(); } m_runningExpressions.clear(); changeStatus(Cantor::Session::Done); } Cantor::Expression *JuliaSession::evaluateExpression( const QString &cmd, Cantor::Expression::FinishingBehavior behave, bool internal) { JuliaExpression *expr = new JuliaExpression(this, internal); changeStatus(Cantor::Session::Running); expr->setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } Cantor::CompletionObject *JuliaSession::completionFor( const QString &command, int index) { return new JuliaCompletionObject(command, index, this); } QSyntaxHighlighter *JuliaSession::syntaxHighlighter(QObject *parent) { JuliaHighlighter *highlighter = new JuliaHighlighter(parent); QObject::connect( this, SIGNAL(updateHighlighter()), highlighter, SLOT(updateHighlight()) ); return highlighter; } void JuliaSession::runJuliaCommand(const QString &command) const { m_interface->call(QLatin1String("runJuliaCommand"), command); } void JuliaSession::runJuliaCommandAsync(const QString &command) { m_interface->callWithCallback( QLatin1String("runJuliaCommand"), {command}, this, SLOT(onResultReady()) ); } void JuliaSession::onResultReady() { m_currentExpression->finalize(); m_runningExpressions.removeAll(m_currentExpression); listVariables(); changeStatus(Cantor::Session::Done); } void JuliaSession::runExpression(JuliaExpression *expr) { m_runningExpressions.append(expr); m_currentExpression = expr; runJuliaCommandAsync(expr->command()); } QString JuliaSession::getStringFromServer(const QString &method) { const QDBusReply &reply = m_interface->call(method); return (reply.isValid() ? reply.value() : reply.error().message()); } QString JuliaSession::getOutput() { return getStringFromServer(QLatin1String("getOutput")); } QString JuliaSession::getError() { return getStringFromServer(QLatin1String("getError")); } bool JuliaSession::getWasException() { const QDBusReply &reply = m_interface->call(QLatin1String("getWasException")); return reply.isValid() && reply.value(); } void JuliaSession::listVariables() { JuliaKeywords::instance()->clearVariables(); JuliaKeywords::instance()->clearFunctions(); m_interface->call(QLatin1String("parseModules")); const QStringList& variables = static_cast>(m_interface->call(QLatin1String("variablesList"))).value(); const QStringList& values = static_cast>(m_interface->call(QLatin1String("variableValuesList"))).value(); for (int i = 0; i < variables.size(); i++) { if (i >= values.size()) { qWarning() << "Don't have value for variable from julia server response, something wrong!"; continue; } const QString& name = variables[i]; QString value = values[i]; if (value != JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER) { // Register variable // We use replace here, because julia return data type for some variables, and we need // remove it to make variable view more consistent with the other backends // More info: https://bugs.kde.org/show_bug.cgi?id=377771 m_variableModel->addVariable(name, value.replace(typeVariableInfo,QLatin1String("["))); JuliaKeywords::instance()->addVariable(name); } else m_variableModel->removeVariable(name); } const QStringList& functions = static_cast>(m_interface->call(QLatin1String("functionsList"))).value(); foreach (const QString& name, functions) { JuliaKeywords::instance()->addFunction(name); } emit updateHighlighter(); } QAbstractItemModel *JuliaSession::variableModel() { return m_variableModel; } bool JuliaSession::integratePlots() { return JuliaSettings::integratePlots(); } diff --git a/src/backends/python/pythonhighlighter.cpp b/src/backends/python/pythonhighlighter.cpp index 9d1567f2..9c8482fb 100644 --- a/src/backends/python/pythonhighlighter.cpp +++ b/src/backends/python/pythonhighlighter.cpp @@ -1,154 +1,156 @@ /* 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) 2013 Filipe Saraiva */ #include "pythonhighlighter.h" #include "pythonkeywords.h" #include #include PythonHighlighter::PythonHighlighter(QObject* parent) : Cantor::DefaultHighlighter(parent) { qDebug() << "PythonHighlighter constructor"; addRule(QRegExp(QLatin1String("\\b\\w+(?=\\()")), functionFormat()); //Code highlighting the different keywords addKeywords(PythonKeywords::instance()->keywords()); addFunctions(PythonKeywords::instance()->functions()); addVariables(PythonKeywords::instance()->variables()); } void PythonHighlighter::highlightBlock(const QString &text) { if (skipHighlighting(text)) { return; } // Do some backend independent highlighting (brackets etc.) DefaultHighlighter::highlightBlock(text); const int IN_MULTILINE_COMMENT = 1; const int IN_SMALL_QUOTE_STRING = 2; const int IN_SINGLE_QUOTE_STRING = 4; const int IN_TRIPLE_QUOTE_STRING = 8; QRegExp multiLineCommentStartEnd(QLatin1String("'''")); QRegExp smallQuoteStartEnd(QLatin1String("'")); QRegExp singleQuoteStringStartEnd(QLatin1String("\"")); QRegExp tripleQuoteStringStartEnd(QLatin1String("\"\"\"")); QRegExp singleLineCommentStart(QLatin1String("#")); int state = previousBlockState(); if (state == -1) { state = 0; } QList flags = { IN_TRIPLE_QUOTE_STRING, IN_SINGLE_QUOTE_STRING, IN_SMALL_QUOTE_STRING, IN_MULTILINE_COMMENT }; QList regexps = { tripleQuoteStringStartEnd, singleQuoteStringStartEnd, smallQuoteStartEnd, multiLineCommentStartEnd }; QList formats = { stringFormat(), stringFormat(), stringFormat(), commentFormat() }; int pos = 0; while (pos < text.length()) { // Trying to close current environments bool triggered = false; for (int i = 0; i < flags.size() && !triggered; i++) { int flag = flags[i]; QRegExp ®exp = regexps[i]; QTextCharFormat &format = formats[i]; if (state & flag) { int new_pos = regexp.indexIn(text, pos); int length; if (new_pos == -1) { length = text.length() - pos; } else { length = new_pos - pos + regexp.matchedLength(); state -= flag; } setFormat(pos, length, format); pos = pos + length; triggered = true; } } if (triggered) { continue; } QRegExp *minRegexp = nullptr; int minPos = INT_MAX; int minIdx = -1; for (int i = 0; i < regexps.size(); i++) { QRegExp ®exp = regexps[i]; int newPos = regexp.indexIn(text, pos); if (newPos != -1) { minPos = qMin(minPos, newPos); minRegexp = ®exp; minIdx = i; } } int singleLineCommentStartPos = singleLineCommentStart.indexIn(text, pos); if (singleLineCommentStartPos != -1 && singleLineCommentStartPos < minPos) { setFormat(pos, text.length() - pos, commentFormat()); break; } else if (minRegexp) { state += flags[minIdx]; pos = minPos + minRegexp->matchedLength(); setFormat(minPos, minRegexp->matchedLength(), formats[minIdx]); } else { break; } } setCurrentBlockState(state); } void PythonHighlighter::updateHighlight() { - - if (!m_variables.isEmpty()) - { - addVariables(m_variables); - m_variables.clear(); - rehighlight(); - } + addVariables(m_variables); + rehighlight(); } void PythonHighlighter::addVariable(const QString variable) { m_variables << variable; } + +void PythonHighlighter::clearVariables() +{ + removeRules(m_variables); + m_variables.clear(); + rehighlight(); +} diff --git a/src/backends/python/pythonhighlighter.h b/src/backends/python/pythonhighlighter.h index 38eb2112..8085de8e 100644 --- a/src/backends/python/pythonhighlighter.h +++ b/src/backends/python/pythonhighlighter.h @@ -1,48 +1,49 @@ /* 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) 2013 Filipe Saraiva */ #ifndef _PYTHONHIGHLIGHTER_H #define _PYTHONHIGHLIGHTER_H #include "defaulthighlighter.h" class PythonHighlighter : public Cantor::DefaultHighlighter { Q_OBJECT public: explicit PythonHighlighter(QObject* parent); ~PythonHighlighter() override = default; public Q_SLOTS: void updateHighlight(); void addVariable(const QString variable); + void clearVariables(); protected: void highlightBlock(const QString& text) override; private: QRegExp commentStartExpression; QRegExp commentEndExpression; QStringList m_variables; }; #endif /* _PYTHONHIGHLIGHTER_H */ diff --git a/src/backends/python/pythonsession.cpp b/src/backends/python/pythonsession.cpp index d7f17b23..f0e51051 100644 --- a/src/backends/python/pythonsession.cpp +++ b/src/backends/python/pythonsession.cpp @@ -1,427 +1,431 @@ /* 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) 2012 Filipe Saraiva Copyright (C) 2015 Minh Ngo */ #include "pythonsession.h" #include "pythonexpression.h" #include "pythonhighlighter.h" #include "pythoncompletionobject.h" #include "pythonkeywords.h" #include "pythonutils.h" #include #include #include #include #include #include #include #include #include PythonSession::PythonSession(Cantor::Backend* backend, int pythonVersion, const QString serverName, const QString DbusChannelName) : Session(backend) , m_variableModel(new Cantor::DefaultVariableModel(this)) , m_currentExpression(nullptr) , m_pIface(nullptr) , m_pProcess(nullptr) , serverName(serverName) , DbusChannelName(DbusChannelName) { if (pythonVersion == 2) PythonKeywords::instance()->python2Mode(); } void PythonSession::login() { qDebug()<<"login"; emit loginStarted(); // TODO: T6113, T6114 if (m_pProcess) m_pProcess->deleteLater(); m_pProcess = new KProcess(this); m_pProcess->setOutputChannelMode(KProcess::SeparateChannels); (*m_pProcess) << QStandardPaths::findExecutable(serverName); m_pProcess->start(); m_pProcess->waitForStarted(); m_pProcess->waitForReadyRead(); QTextStream stream(m_pProcess->readAllStandardOutput()); const QString& readyStatus = QString::fromLatin1("ready"); while (m_pProcess->state() == QProcess::Running) { const QString& rl = stream.readLine(); if (rl == readyStatus) break; } if (!QDBusConnection::sessionBus().isConnected()) { qWarning() << "Can't connect to the D-Bus session bus.\n" "To start it, run: eval `dbus-launch --auto-syntax`"; return; } const QString& serviceName = DbusChannelName + QString::fromLatin1("-%1").arg(m_pProcess->pid()); m_pIface = new QDBusInterface(serviceName, QString::fromLatin1("/"), QString(), QDBusConnection::sessionBus()); if (!m_pIface->isValid()) { qWarning() << QDBusConnection::sessionBus().lastError().message(); return; } m_pIface->call(QString::fromLatin1("login")); m_pIface->call(QString::fromLatin1("setFilePath"), worksheetPath); const QStringList& scripts = autorunScripts(); if(!scripts.isEmpty()){ QString autorunScripts = scripts.join(QLatin1String("\n")); getPythonCommandOutput(autorunScripts); } const QString& importerFile = QLatin1String(":py/import_default_modules.py"); evaluateExpression(fromSource(importerFile), Cantor::Expression::DeleteOnFinish, true); listVariables(); changeStatus(Session::Done); emit loginDone(); } void PythonSession::logout() { // TODO: T6113, T6114 m_pProcess->terminate(); + m_variableModel->clearVariables(); + emit clearVariables(); + qDebug()<<"logout"; changeStatus(Status::Disable); } void PythonSession::interrupt() { // TODO: T6113, T6114 if (m_pProcess->pid()) m_pProcess->kill(); qDebug()<<"interrupt"; foreach(Cantor::Expression* e, m_runningExpressions) e->interrupt(); m_runningExpressions.clear(); changeStatus(Cantor::Session::Done); } Cantor::Expression* PythonSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal) { qDebug() << "evaluating: " << cmd; PythonExpression* expr = new PythonExpression(this, internal); changeStatus(Cantor::Session::Running); expr->setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } void PythonSession::runExpression(PythonExpression* expr) { qDebug() << "run expression"; m_currentExpression = expr; const QString& command = expr->command(); QStringList commandLine = command.split(QLatin1String("\n")); QString commandProcessing; for(const QString& command : commandLine){ const QString firstLineWord = command.trimmed().replace(QLatin1String("("), QLatin1String(" ")) .split(QLatin1String(" ")).at(0); // Ignore comments if (firstLineWord.length() != 0 && firstLineWord[0] == QLatin1Char('#')){ commandProcessing += command + QLatin1String("\n"); continue; } if(firstLineWord.contains(QLatin1String("execfile"))){ commandProcessing += command; continue; } commandProcessing += command + QLatin1String("\n"); } readExpressionOutput(commandProcessing); } // Is called asynchronously in the Python3 plugin void PythonSession::readExpressionOutput(const QString& commandProcessing) { readOutput(commandProcessing); } void PythonSession::getPythonCommandOutput(const QString& commandProcessing) { runPythonCommand(commandProcessing); m_output = getOutput(); m_error = getError(); } bool PythonSession::identifyKeywords(const QString& command) { QString verifyErrorImport; QString listKeywords; QString keywordsString; QString moduleImported; QString moduleVariable; getPythonCommandOutput(command); qDebug() << "verifyErrorImport: "; if(!m_error.isEmpty()){ qDebug() << "returned false"; return false; } moduleImported += identifyPythonModule(command); moduleVariable += identifyVariableModule(command); if((moduleVariable.isEmpty()) && (!command.endsWith(QLatin1String("*")))){ keywordsString = command.section(QLatin1String(" "), 3).remove(QLatin1String(" ")); } if(moduleVariable.isEmpty() && (command.endsWith(QLatin1String("*")))){ listKeywords += QString::fromLatin1("import %1\n" \ "print(dir(%1))\n" \ "del %1\n").arg(moduleImported); } if(!moduleVariable.isEmpty()){ listKeywords += QLatin1String("print(dir(") + moduleVariable + QLatin1String("))\n"); } if(!listKeywords.isEmpty()){ getPythonCommandOutput(listKeywords); keywordsString = m_output; keywordsString.remove(QLatin1String("'")); keywordsString.remove(QLatin1String(" ")); keywordsString.remove(QLatin1String("[")); keywordsString.remove(QLatin1String("]")); } QStringList keywordsList = keywordsString.split(QLatin1String(",")); PythonKeywords::instance()->loadFromModule(moduleVariable, keywordsList); qDebug() << "Module imported" << moduleImported; return true; } QString PythonSession::identifyPythonModule(const QString& command) const { QString module; if(command.contains(QLatin1String("import "))){ module = command.section(QLatin1String(" "), 1, 1); } qDebug() << "module identified" << module; return module; } QString PythonSession::identifyVariableModule(const QString& command) const { QString variable; if(command.contains(QLatin1String("import "))){ variable = command.section(QLatin1String(" "), 1, 1); } if((command.contains(QLatin1String("import "))) && (command.contains(QLatin1String(" as ")))){ variable = command.section(QLatin1String(" "), 3, 3); } if(command.contains(QLatin1String("from "))){ variable = QLatin1String(""); } qDebug() << "variable identified" << variable; return variable; } void PythonSession::expressionFinished() { qDebug()<< "finished"; PythonExpression* expression = qobject_cast(sender()); m_runningExpressions.removeAll(expression); qDebug() << "size: " << m_runningExpressions.size(); } void PythonSession::updateOutput() { if(m_error.isEmpty()){ m_currentExpression->parseOutput(m_output); qDebug() << "output: " << m_output; } else { m_currentExpression->parseError(m_error); qDebug() << "error: " << m_error; } listVariables(); changeStatus(Cantor::Session::Done); } void PythonSession::readOutput(const QString& commandProcessing) { qDebug() << "readOutput"; getPythonCommandOutput(commandProcessing); updateOutput(); } void PythonSession::listVariables() { const QString& listVariableCommand = QLatin1String( "try: \n" " import numpy \n" " __cantor_numpy_internal__ = numpy.get_printoptions()['threshold'] \n" " numpy.set_printoptions(threshold=100000000) \n" "except ModuleNotFoundError: \n" " pass \n" "print(globals()) \n" "try: \n" " import numpy \n" " numpy.set_printoptions(threshold=__cantor_numpy_internal__) \n" " del __cantor_numpy_internal__ \n" "except ModuleNotFoundError: \n" " pass \n" ); getPythonCommandOutput(listVariableCommand); qDebug() << m_output; m_output.remove(QLatin1String("{")); m_output.remove(QLatin1String("<")); m_output.remove(QLatin1String(">")); m_output.remove(QLatin1String("}")); foreach(QString line, m_output.split(QLatin1String(", '"))){ QStringList parts = line.simplified().split(QLatin1String(":")); const QString& first = parts.first(); const QString& last = parts.last(); if(!first.startsWith(QLatin1String("'__")) && !first.startsWith(QLatin1String("__")) && !first.startsWith(QLatin1String("CatchOutPythonBackend'")) && !first.startsWith(QLatin1String("errorPythonBackend'")) && !first.startsWith(QLatin1String("outputPythonBackend'")) && !last.startsWith(QLatin1String(" class ")) && !last.startsWith(QLatin1String(" function ")) && !last.startsWith(QLatin1String(" module '")) /*skip imported modules*/ ) { m_variableModel->addVariable(parts.first().remove(QLatin1String("'")).simplified(), parts.last().simplified()); emit newVariable(parts.first().remove(QLatin1String("'")).simplified()); } } emit updateHighlighter(); } QSyntaxHighlighter* PythonSession::syntaxHighlighter(QObject* parent) { PythonHighlighter* highlighter = new PythonHighlighter(parent); QObject::connect(this, SIGNAL(updateHighlighter()), highlighter, SLOT(updateHighlight())); QObject::connect(this, SIGNAL(newVariable(QString)), highlighter, SLOT(addVariable(QString))); + connect(this, &PythonSession::clearVariables, highlighter, &PythonHighlighter::clearVariables); return highlighter; } Cantor::CompletionObject* PythonSession::completionFor(const QString& command, int index) { return new PythonCompletionObject(command, index, this); } QAbstractItemModel* PythonSession::variableModel() { return m_variableModel; } void PythonSession::runPythonCommand(const QString& command) const { // TODO: T6113, T6114 m_pIface->call(QString::fromLatin1("runPythonCommand"), command); } QString PythonSession::getOutput() const { // TODO: T6113, T6114 const QDBusReply& reply = m_pIface->call(QString::fromLatin1("getOutput")); if (reply.isValid()) return reply.value(); return reply.error().message(); } QString PythonSession::getError() const { // TODO: T6113, T6114 const QDBusReply& reply = m_pIface->call(QString::fromLatin1("getError")); if (reply.isValid()) return reply.value(); return reply.error().message(); } void PythonSession::setWorksheetPath(const QString& path) { worksheetPath = path; } diff --git a/src/backends/python/pythonsession.h b/src/backends/python/pythonsession.h index 2f8c8d1c..013091b0 100644 --- a/src/backends/python/pythonsession.h +++ b/src/backends/python/pythonsession.h @@ -1,104 +1,105 @@ /* 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) 2012 Filipe Saraiva Copyright (C) 2015 Minh Ngo */ #ifndef _PYTHONSESSION_H #define _PYTHONSESSION_H #include "session.h" #include #include namespace Cantor { class DefaultVariableModel; } class PythonExpression; class KDirWatch; class QDBusInterface; class KProcess; class CANTOR_PYTHONBACKEND_EXPORT PythonSession : public Cantor::Session { Q_OBJECT public: PythonSession(Cantor::Backend* backend, int pythonVersion, const QString serverName, const QString DbusChannelName); ~PythonSession() override = default; void login() override; void logout() override; void interrupt() override; void runExpression(PythonExpression* expr); 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 setWorksheetPath(const QString& path) override; virtual bool integratePlots() const = 0; virtual QStringList autorunScripts() const = 0; private: Cantor::DefaultVariableModel* m_variableModel; QList m_runningExpressions; PythonExpression* m_currentExpression; QDBusInterface* m_pIface; KProcess* m_pProcess; QString serverName; QString DbusChannelName; QString worksheetPath; protected: QString m_output; QString m_error; private: void listVariables(); void getPythonCommandOutput(const QString& commandProcessing); QString identifyPythonModule(const QString& command) const; QString identifyVariableModule(const QString& command) const; bool identifyKeywords(const QString& command); void runPythonCommand(const QString& command) const; QString getOutput() const; QString getError() const; virtual void readExpressionOutput(const QString& commandProcessing); protected: void updateOutput(); private Q_SLOTS: void readOutput(const QString& commandProcessing); void expressionFinished(); Q_SIGNALS: void updateHighlighter(); void newVariable(const QString variable); + void clearVariables(); }; #endif /* _PYTHONSESSION_H */ diff --git a/src/backends/scilab/scilabsession.cpp b/src/backends/scilab/scilabsession.cpp index 43d0b52a..90774bec 100644 --- a/src/backends/scilab/scilabsession.cpp +++ b/src/backends/scilab/scilabsession.cpp @@ -1,272 +1,273 @@ /* 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) 2011 Filipe Saraiva */ #include "scilabsession.h" #include "scilabexpression.h" #include "scilabhighlighter.h" #include "scilabcompletionobject.h" #include #include #include #include #include #include #include #include #include #include #include ScilabSession::ScilabSession( Cantor::Backend* backend) : Session(backend), m_process(nullptr), m_watch(nullptr), m_variableModel(new Cantor::DefaultVariableModel(this)) { } ScilabSession::~ScilabSession() { if (m_process) m_process->terminate(); } void ScilabSession::login() { qDebug()<<"login"; emit loginStarted(); QStringList args; args << QLatin1String("-nb"); m_process = new QProcess(this); m_process->setArguments(args); m_process->setProgram(ScilabSettings::self()->path().toLocalFile()); qDebug() << m_process->program(); m_process->setProcessChannelMode(QProcess::SeparateChannels); m_process->start(); m_process->waitForStarted(); m_process->waitForReadyRead(); if(ScilabSettings::integratePlots()){ qDebug() << "integratePlots"; QString tempPath = QDir::tempPath(); QString pathScilabOperations = tempPath; pathScilabOperations.prepend(QLatin1String("chdir('")); pathScilabOperations.append(QLatin1String("');\n")); qDebug() << "Processing command to change chdir in Scilab. Command " << pathScilabOperations.toLocal8Bit(); m_process->write(pathScilabOperations.toLocal8Bit()); m_watch = new KDirWatch(this); m_watch->setObjectName(QLatin1String("ScilabDirWatch")); m_watch->addDir(tempPath, KDirWatch::WatchFiles); qDebug() << "addDir " << tempPath << "? " << m_watch->contains(QLatin1String(tempPath.toLocal8Bit())); QObject::connect(m_watch, &KDirWatch::created, this, &ScilabSession::plotFileChanged); } if(!ScilabSettings::self()->autorunScripts().isEmpty()){ QString autorunScripts = ScilabSettings::self()->autorunScripts().join(QLatin1String("\n")); m_process->write(autorunScripts.toLocal8Bit()); } QObject::connect(m_process, &QProcess::readyReadStandardOutput, this, &ScilabSession::readOutput); QObject::connect(m_process, &QProcess::readyReadStandardError, this, &ScilabSession::readError); m_process->readAllStandardOutput().clear(); m_process->readAllStandardError().clear(); changeStatus(Cantor::Session::Done); emit loginDone(); } void ScilabSession::logout() { qDebug()<<"logout"; m_process->write("exit\n"); expressionQueue().clear(); + m_variableModel->clearVariables(); QDir removePlotFigures; QListIterator i(m_listPlotName); while(i.hasNext()){ removePlotFigures.remove(QLatin1String(i.next().toLocal8Bit().constData())); } changeStatus(Status::Disable); } void ScilabSession::interrupt() { qDebug()<<"interrupt"; if (status() == Cantor::Session::Running) expressionQueue().first()->interrupt(); changeStatus(Cantor::Session::Done); } Cantor::Expression* ScilabSession::evaluateExpression(const QString& cmd, Cantor::Expression::FinishingBehavior behave, bool internal) { qDebug() << "evaluating: " << cmd; ScilabExpression* expr = new ScilabExpression(this, internal); expr->setFinishingBehavior(behave); expr->setCommand(cmd); expr->evaluate(); return expr; } void ScilabSession::runFirstExpression() { qDebug() <<"call runFirstExpression"; qDebug() << "m_process: " << m_process; qDebug() << "status: " << (status() == Cantor::Session::Running ? "Running" : "Done"); if (!m_process) return; qDebug()<<"running next expression"; if(!expressionQueue().isEmpty()) { ScilabExpression* expr = static_cast(expressionQueue().first()); QString command; command.prepend(QLatin1String("\nprintf('begin-cantor-scilab-command-processing')\n")); command += expr->command(); command += QLatin1String("\nprintf('terminated-cantor-scilab-command-processing')\n"); connect(expr, &ScilabExpression::statusChanged, this, &ScilabSession::currentExpressionStatusChanged); expr->setStatus(Cantor::Expression::Computing); qDebug() << "Writing command to process" << command; m_process->write(command.toLocal8Bit()); } } void ScilabSession::readError() { qDebug() << "readError"; QString error = QLatin1String(m_process->readAllStandardError()); qDebug() << "error: " << error; static_cast(expressionQueue().first())->parseError(error); } void ScilabSession::readOutput() { qDebug() << "readOutput"; while(m_process->bytesAvailable() > 0){ m_output.append(QString::fromLocal8Bit(m_process->readLine())); } qDebug() << "output.isNull? " << m_output.isNull(); qDebug() << "output: " << m_output; if(status() != Running || m_output.isNull()){ return; } if(m_output.contains(QLatin1String("begin-cantor-scilab-command-processing")) && m_output.contains(QLatin1String("terminated-cantor-scilab-command-processing"))){ m_output.remove(QLatin1String("begin-cantor-scilab-command-processing")); m_output.remove(QLatin1String("terminated-cantor-scilab-command-processing")); static_cast(expressionQueue().first())->parseOutput(m_output); m_output.clear(); } } void ScilabSession::plotFileChanged(const QString& filename) { qDebug() << "plotFileChanged filename:" << filename; if (expressionQueue().first() && (filename.contains(QLatin1String("cantor-export-scilab-figure")))){ qDebug() << "Calling parsePlotFile"; static_cast(expressionQueue().first())->parsePlotFile(filename); m_listPlotName.append(filename); } } void ScilabSession::currentExpressionStatusChanged(Cantor::Expression::Status status) { qDebug() << "currentExpressionStatusChanged: " << status; switch (status){ case Cantor::Expression::Computing: case Cantor::Expression::Interrupted: case Cantor::Expression::Queued: break; case Cantor::Expression::Done: case Cantor::Expression::Error: expressionQueue().removeFirst(); if (expressionQueue().isEmpty()) changeStatus(Done); else runFirstExpression(); break; } } QSyntaxHighlighter* ScilabSession::syntaxHighlighter(QObject* parent) { ScilabHighlighter *highlighter = new ScilabHighlighter(parent, this); return highlighter; } Cantor::CompletionObject* ScilabSession::completionFor(const QString& command, int index) { return new ScilabCompletionObject(command, index, this); } QAbstractItemModel* ScilabSession::variableModel() { return m_variableModel; }