diff --git a/src/backends/octave/octaveexpression.cpp b/src/backends/octave/octaveexpression.cpp index c09a563f..b8a92b8d 100644 --- a/src/backends/octave/octaveexpression.cpp +++ b/src/backends/octave/octaveexpression.cpp @@ -1,178 +1,186 @@ /* Copyright (C) 2010 Miha Čančula 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. */ #include "octaveexpression.h" #include "octavesession.h" #include "defaultvariablemodel.h" #include "textresult.h" #include #include #include static const QLatin1String printCommand("cantor_print();"); OctaveExpression::OctaveExpression(Cantor::Session* session, bool internal): Expression(session, internal), m_plotPending(false), m_finished(false) { m_plotCommands << QLatin1String("plot") << QLatin1String("semilogx") << QLatin1String("semilogy") << QLatin1String("loglog") << QLatin1String("polar") << QLatin1String("contour") << QLatin1String("bar") << QLatin1String("stairs") << QLatin1String("errorbar") << QLatin1String("sombrero") << QLatin1String("hist") << QLatin1String("fplot") << QLatin1String("imshow") << QLatin1String("stem") << QLatin1String("stem3") << QLatin1String("scatter") << QLatin1String("pareto") << QLatin1String("rose") << QLatin1String("pie") << QLatin1String("quiver") << QLatin1String("compass") << QLatin1String("feather") << QLatin1String("pcolor") << QLatin1String("area") << QLatin1String("fill") << QLatin1String("comet") << QLatin1String("plotmatrix") /* 3d-plots */ << QLatin1String("plot3") << QLatin1String("mesh") << QLatin1String("meshc") << QLatin1String("meshz") << QLatin1String("surf") << QLatin1String("surfc") << QLatin1String("surfl") << QLatin1String("surfnorm") << QLatin1String("isosurface")<< QLatin1String("isonormals") << QLatin1String("isocaps") /* 3d-plots defined by a function */ << QLatin1String("ezplot3") << QLatin1String("ezmesh") << QLatin1String("ezmeshc") << QLatin1String("ezsurf") << QLatin1String("ezsurfc"); m_plotCommands << QLatin1String("cantor_plot2d") << QLatin1String("cantor_plot3d"); } void OctaveExpression::interrupt() { qDebug() << "interrupt"; setStatus(Interrupted); } void OctaveExpression::evaluate() { qDebug() << "evaluate"; QString cmd = command(); QStringList cmdWords = cmd.split(QRegExp(QLatin1String("\\b")), QString::SkipEmptyParts); if (!cmdWords.contains(QLatin1String("help")) && !cmdWords.contains(QLatin1String("completion_matches"))) { foreach (const QString& plotCmd, m_plotCommands) { if (cmdWords.contains(plotCmd)) { setPlotPending(true); qDebug() << "Executing a plot command"; break; } } } m_finished = false; session()->enqueueExpression(this); } QString OctaveExpression::internalCommand() { QString cmd = command(); if (m_plotPending && !cmd.contains(QLatin1String("cantor_plot")) && !cmd.contains(printCommand)) { if (!cmd.endsWith(QLatin1Char(';')) && !cmd.endsWith(QLatin1Char(','))) cmd += QLatin1Char(','); cmd += printCommand; } // We need remove all comments here, because below we merge all strings to one long string // Otherwise, all code after line with comment will be commented out after merging // So, this small state machine remove all comments // FIXME better implementation QString tmp; // 0 - command mode, 1 - string mode for ', 2 - string mode for ", 3 - comment mode int status = 0; for (int i = 0; i < cmd.size(); i++) { const char ch = cmd[i].toLatin1(); if (status == 0 && (ch == '#' || ch == '%')) status = 3; else if (status == 0 && ch == '\'') status = 1; else if (status == 0 && ch == '"') status = 2; else if (status == 1 && ch == '\'') status = 0; else if (status == 2 && ch == '"') status = 0; else if (status == 3 && ch == '\n') status = 0; if (status != 3) tmp += cmd[i]; } cmd = tmp; cmd.replace(QLatin1String(";\n"), QLatin1String(";")); cmd.replace(QLatin1Char('\n'), QLatin1Char(',')); cmd += QLatin1Char('\n'); return cmd; } void OctaveExpression::parseOutput(const QString& output) { qDebug() << "parseOutput: " << output; if (!output.trimmed().isEmpty()) { // TODO: what about help in comment? printf with '... help ...'? // This must be corrected. if (command().contains(QLatin1String("help"))) { - setResult(new Cantor::HelpResult(output)); + addResult(new Cantor::HelpResult(output)); } else { - setResult(new Cantor::TextResult(output)); + addResult(new Cantor::TextResult(output)); } } m_finished = true; if (!m_plotPending) setStatus(Done); } void OctaveExpression::parseError(const QString& error) { - setErrorMessage(error); - setStatus(Error); + if (error.startsWith(QLatin1String("warning: "))) + { + // It's warning, so add as result + addResult(new Cantor::TextResult(error)); + } + else + { + setErrorMessage(error); + setStatus(Error); + } } void OctaveExpression::parsePlotFile(const QString& file) { qDebug() << "parsePlotFile"; if (QFile::exists(file)) { qDebug() << "OctaveExpression::parsePlotFile: " << file; - setResult(new OctavePlotResult(QUrl::fromLocalFile(file))); + addResult(new OctavePlotResult(QUrl::fromLocalFile(file))); setPlotPending(false); if (m_finished) { setStatus(Done); } } } void OctaveExpression::setPlotPending(bool plot) { m_plotPending = plot; } diff --git a/src/backends/octave/octavesession.cpp b/src/backends/octave/octavesession.cpp index d1e917f3..4f2c2f77 100644 --- a/src/backends/octave/octavesession.cpp +++ b/src/backends/octave/octavesession.cpp @@ -1,369 +1,380 @@ /* Copyright (C) 2010 Miha Čančula 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. */ #include "octavesession.h" #include "octaveexpression.h" #include "octavecompletionobject.h" #include "octavesyntaxhelpobject.h" #include "octavehighlighter.h" #include "result.h" #include "textresult.h" #include "settings.h" #include "octavehighlighter.h" #include #include #include #include #include +#include +#include #ifndef Q_OS_WIN #include #endif #include "octavevariablemodel.h" const QRegExp OctaveSession::PROMPT_UNCHANGEABLE_COMMAND = QRegExp(QLatin1String("(,|;)+")); OctaveSession::OctaveSession ( Cantor::Backend* backend ) : Session ( backend), m_process(nullptr), m_prompt(QLatin1String("CANTOR_OCTAVE_BACKEND_PROMPT:([0-9]+)> ")), m_subprompt(QLatin1String("CANTOR_OCTAVE_BACKEND_SUBPROMPT:([0-9]+)> ")), m_previousPromptNumber(1), m_watch(nullptr), m_syntaxError(false) { setVariableModel(new OctaveVariableModel(this)); } void OctaveSession::login() { qDebug() << "login"; emit loginStarted(); m_process = new KProcess ( this ); QStringList args; args << QLatin1String("--silent"); args << QLatin1String("--interactive"); args << QLatin1String("--persist"); // Setting prompt and subprompt args << QLatin1String("--eval"); args << QLatin1String("PS1('CANTOR_OCTAVE_BACKEND_PROMPT:\\#> ');"); args << QLatin1String("--eval"); args << QLatin1String("PS2('CANTOR_OCTAVE_BACKEND_SUBPROMPT:\\#> ');"); - // Add the cantor script directory to search path - const QString& octaveScriptInstallDir = locateCantorFile(QLatin1String("octavebackend"), QStandardPaths::LocateDirectory); - qDebug() << "using octaveScriptInstallDir: " << octaveScriptInstallDir; - args << QLatin1String("--eval"); - args << QString::fromLatin1("addpath \"%1\";").arg(octaveScriptInstallDir); + // Add the cantor script directory to octave script search path + const QStringList& scriptDirs = locateAllCantorFiles(QLatin1String("octavebackend"), QStandardPaths::LocateDirectory); + if (scriptDirs.isEmpty()) + qCritical() << "Octave script directory not found, needed for integrated plots"; + else + { + for (const QString& dir : scriptDirs) + args << QLatin1String("--eval") << QString::fromLatin1("addpath \"%1\";").arg(dir); + } // Print the temp dir, used for plot files args << QLatin1String("--eval"); args << QLatin1String("printf('%s\\n', ['____TMP_DIR____ = ' tempdir]);"); // Do not show extra text in help commands args << QLatin1String("--eval"); args << QLatin1String("suppress_verbose_help_message(1);"); if (OctaveSettings::integratePlots()) { // Do not show the popup when plotting, rather only print to a file args << QLatin1String("--eval"); args << QLatin1String("set (0, \"defaultfigurevisible\",\"off\");"); args << QLatin1String("--eval"); args << QLatin1String("graphics_toolkit gnuplot;"); } else { args << QLatin1String("--eval"); args << QLatin1String("set (0, \"defaultfigurevisible\",\"on\");"); } if (OctaveSettings::integratePlots()) { m_watch = new KDirWatch(this); m_watch->setObjectName(QLatin1String("OctaveDirWatch")); connect (m_watch, SIGNAL(dirty(QString)), SLOT(plotFileChanged(QString)) ); } m_process->setProgram ( OctaveSettings::path().toLocalFile(), args ); qDebug() << "starting " << m_process->program(); m_process->setOutputChannelMode ( KProcess::SeparateChannels ); m_process->start(); m_process->waitForStarted(); // Got tmp dir bool loginFinished = false; QString input; while (!loginFinished) { m_process->waitForReadyRead(); input += QString::fromLatin1(m_process->readAllStandardOutput()); qDebug() << "login input: " << input; if (input.contains(QLatin1String("____TMP_DIR____"))) { m_tempDir = input; m_tempDir.remove(0,18); m_tempDir.chop(1); // isolate the tempDir's location qDebug() << "Got temporary file dir:" << m_tempDir; if (m_watch) { m_watch->addDir(m_tempDir, KDirWatch::WatchFiles); } loginFinished = true; } } connect ( m_process, SIGNAL (readyReadStandardOutput()), SLOT (readOutput()) ); connect ( m_process, SIGNAL (readyReadStandardError()), SLOT (readError()) ); connect ( m_process, SIGNAL (error(QProcess::ProcessError)), SLOT (processError()) ); if(!OctaveSettings::self()->autorunScripts().isEmpty()){ QString autorunScripts = OctaveSettings::self()->autorunScripts().join(QLatin1String("\n")); evaluateExpression(autorunScripts, OctaveExpression::DeleteOnFinish, true); updateVariables(); } changeStatus(Cantor::Session::Done); emit loginDone(); qDebug()<<"login done"; } void OctaveSession::logout() { qDebug()<<"logout"; if(!m_process) return; disconnect(m_process, nullptr, this, nullptr); // if(status()==Cantor::Session::Running) // TODO: terminate the running expressions first m_process->write("exit\n"); qDebug()<<"waiting for octave to finish"; m_process->waitForFinished(); qDebug()<<"octave exit finished"; if(m_process->state() != QProcess::NotRunning) { m_process->kill(); qDebug()<<"octave still running, process kill enforced"; } expressionQueue().clear(); delete m_process; m_process = nullptr; m_tempDir.clear(); m_output.clear(); m_previousPromptNumber = 1; variableModel()->clearVariables(); changeStatus(Status::Disable); qDebug()<<"logout done"; } void OctaveSession::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(); // Cleanup inner state and call octave prompt printing // If we move this code for interruption to Session, we need add function for // cleaning before setting Done status m_output.clear(); m_process->write("\n"); qDebug()<<"done interrupting"; } changeStatus(Cantor::Session::Done); } void OctaveSession::processError() { qDebug() << "processError"; emit error(m_process->errorString()); } Cantor::Expression* OctaveSession::evaluateExpression ( const QString& command, Cantor::Expression::FinishingBehavior finishingBehavior, bool internal ) { qDebug() << "evaluating: " << command; OctaveExpression* expression = new OctaveExpression ( this, internal); expression->setCommand ( command ); expression->setFinishingBehavior ( finishingBehavior ); expression->evaluate(); return expression; } void OctaveSession::runFirstExpression() { OctaveExpression* expression = static_cast(expressionQueue().first()); connect(expression, SIGNAL(statusChanged(Cantor::Expression::Status)), this, SLOT(currentExpressionStatusChanged(Cantor::Expression::Status))); QString command = expression->internalCommand(); expression->setStatus(Cantor::Expression::Computing); if (isDoNothingCommand(command)) expression->setStatus(Cantor::Expression::Done); else { m_process->write ( command.toLocal8Bit() ); } } void OctaveSession::readError() { qDebug() << "readError"; QString error = QString::fromLocal8Bit(m_process->readAllStandardError()); if (!expressionQueue().isEmpty() && !error.isEmpty()) { OctaveExpression* const exp = static_cast(expressionQueue().first()); if (m_syntaxError) { m_syntaxError = false; exp->parseError(i18n("Syntax Error")); } else exp->parseError(error); m_output.clear(); } } void OctaveSession::readOutput() { qDebug() << "readOutput"; while (m_process->bytesAvailable() > 0) { QString line = QString::fromLocal8Bit(m_process->readLine()); qDebug()<<"start parsing " << " " << line; if (line.contains(m_prompt)) { const int promptNumber = m_prompt.cap(1).toInt(); + // Add all text before prompt, if exists + m_output += QStringRef(&line, 0, line.indexOf(m_prompt)).toString(); if (!expressionQueue().isEmpty()) { const QString& command = expressionQueue().first()->command(); if (m_previousPromptNumber + 1 == promptNumber || isSpecialOctaveCommand(command)) { if (!expressionQueue().isEmpty()) + { + readError(); static_cast(expressionQueue().first())->parseOutput(m_output); + } } else { // Error command don't increase octave prompt number (usually, but not always) readError(); } } m_previousPromptNumber = promptNumber; m_output.clear(); } else if (line.contains(m_subprompt) && m_subprompt.cap(1).toInt() == m_previousPromptNumber) { // User don't write finished octave statement (for example, write 'a = [1,2, ' only), so // octave print subprompt and waits input finish. m_syntaxError = true; qDebug() << "subprompt catch"; m_process->write(")]'\"\n"); // forse exit from subprompt m_output.clear(); } else m_output += line; } } void OctaveSession::currentExpressionStatusChanged(Cantor::Expression::Status status) { qDebug() << "currentExpressionStatusChanged"; switch (status) { case Cantor::Expression::Done: case Cantor::Expression::Error: finishFirstExpression(); break; default: break; } } void OctaveSession::plotFileChanged(const QString& filename) { if (!QFile::exists(filename) || !filename.split(QLatin1Char('/')).last().contains(QLatin1String("c-ob-"))) { return; } if (!expressionQueue().isEmpty()) { static_cast(expressionQueue().first())->parsePlotFile(filename); } } Cantor::CompletionObject* OctaveSession::completionFor ( const QString& cmd, int index ) { return new OctaveCompletionObject ( cmd, index, this ); } Cantor::SyntaxHelpObject* OctaveSession::syntaxHelpFor ( const QString& cmd ) { return new OctaveSyntaxHelpObject ( cmd, this ); } QSyntaxHighlighter* OctaveSession::syntaxHighlighter ( QObject* parent ) { return new OctaveHighlighter ( parent, this ); } void OctaveSession::runSpecificCommands() { m_process->write("figure(1,'visible','off')"); } bool OctaveSession::isDoNothingCommand(const QString& command) { return PROMPT_UNCHANGEABLE_COMMAND.exactMatch(command) || command.isEmpty() || command == QLatin1String("\n"); } bool OctaveSession::isSpecialOctaveCommand(const QString& command) { return command.contains(QLatin1String("completion_matches")); } diff --git a/src/lib/session.cpp b/src/lib/session.cpp index 04f3f1d9..d39f08bb 100644 --- a/src/lib/session.cpp +++ b/src/lib/session.cpp @@ -1,209 +1,219 @@ /* 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 */ #include "session.h" using namespace Cantor; #include "backend.h" #include "defaultvariablemodel.h" #include #include class Cantor::SessionPrivate { public: SessionPrivate() : backend(nullptr), status(Session::Disable), typesettingEnabled(false), expressionCount(0), variableModel(nullptr), needUpdate(false) { } Backend* backend; Session::Status status; bool typesettingEnabled; int expressionCount; QList expressionQueue; DefaultVariableModel* variableModel; bool needUpdate; }; Session::Session( Backend* backend ) : QObject(backend), d(new SessionPrivate) { d->backend=backend; } Session::Session( Backend* backend, DefaultVariableModel* model) : QObject(backend), d(new SessionPrivate) { d->backend=backend; d->variableModel=model; } Session::~Session() { delete d; } QList& Cantor::Session::expressionQueue() const { return d->expressionQueue; } void Session::enqueueExpression(Expression* expr) { d->expressionQueue.append(expr); //run the newly added expression immediately if it's the only one in the queue if (d->expressionQueue.size() == 1) { changeStatus(Cantor::Session::Running); runFirstExpression(); } else expr->setStatus(Cantor::Expression::Queued); } void Session::runFirstExpression() { } void Session::finishFirstExpression(bool setDoneAfterUpdate) { if (!d->expressionQueue.isEmpty()) d->needUpdate |= !d->expressionQueue.takeFirst()->isInternal(); if (d->expressionQueue.isEmpty()) if (d->variableModel && d->needUpdate) { d->variableModel->update(); d->needUpdate = false; // Some variable models could update internal lists without running expressions // So, if after update queue still empty, set status to Done // setDoneAfterUpdate used for compatibility with some backends, like R if (setDoneAfterUpdate && d->expressionQueue.isEmpty()) changeStatus(Done); } else changeStatus(Done); else runFirstExpression(); } Backend* Session::backend() { return d->backend; } Cantor::Session::Status Session::status() { return d->status; } void Session::changeStatus(Session::Status newStatus) { d->status=newStatus; emit statusChanged(newStatus); } void Session::setTypesettingEnabled(bool enable) { d->typesettingEnabled=enable; } bool Session::isTypesettingEnabled() { return d->typesettingEnabled; } void Session::setWorksheetPath(const QString& path) { Q_UNUSED(path); return; } CompletionObject* Session::completionFor(const QString& cmd, int index) { Q_UNUSED(cmd); Q_UNUSED(index); //Return 0 per default, so Backends not offering tab completions don't have //to reimplement this. This method should only be called on backends with //the Completion Capability flag return nullptr; } SyntaxHelpObject* Session::syntaxHelpFor(const QString& cmd) { Q_UNUSED(cmd); //Return 0 per default, so Backends not offering tab completions don't have //to reimplement this. This method should only be called on backends with //the SyntaxHelp Capability flag return nullptr; } QSyntaxHighlighter* Session::syntaxHighlighter(QObject* parent) { Q_UNUSED(parent); return nullptr; } DefaultVariableModel* Session::variableModel() const { //Return deafult session model per default //By default, variableModel is nullptr, so Backends not offering variable management don't //have to reimplement this. This method should only be called on backends with //VariableManagement Capability flag return d->variableModel; } QAbstractItemModel* Session::variableDataModel() const { return variableModel(); } void Session::updateVariables() { if (d->variableModel) { d->variableModel->update(); d->needUpdate = false; } } void Cantor::Session::setVariableModel(Cantor::DefaultVariableModel* model) { d->variableModel = model; } int Session::nextExpressionId() { return d->expressionCount++; } QString Session::locateCantorFile(const QString& partialPath, QStandardPaths::LocateOptions options) { QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, partialPath, options); if (file.isEmpty()) file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("cantor/") + partialPath, options); return file; } + +QStringList Session::locateAllCantorFiles(const QString& partialPath, QStandardPaths::LocateOptions options) +{ + QStringList files = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, partialPath, options); + + if (files.isEmpty()) + files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("cantor/") + partialPath, options); + + return files; +} diff --git a/src/lib/session.h b/src/lib/session.h index e30f7154..eec8cb57 100644 --- a/src/lib/session.h +++ b/src/lib/session.h @@ -1,261 +1,262 @@ /* 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 _SESSION_H #define _SESSION_H #include #include #include "cantor_export.h" #include "expression.h" #include "defaultvariablemodel.h" class QTextEdit; class QSyntaxHighlighter; class QAbstractItemModel; /** * Namespace collecting all Classes of the Cantor Libraries */ namespace Cantor { class Backend; class SessionPrivate; class CompletionObject; class SyntaxHelpObject; class DefaultVariableModel; /** * The Session object is the main class used to interact with a Backend. * It is used to evaluate Expressions, get completions, syntax highlighting, etc. * * @author Alexander Rieder */ class CANTOR_EXPORT Session : public QObject { Q_OBJECT public: enum Status { Running, ///< the session is busy, running some expression Done, ///< the session has done all the jobs, and is now waiting for more Disable ///< the session don't login yet, or already logout }; /** * Create a new Session. This should not yet set up the complete session, * thats job of the login() function * @see login() */ explicit Session( Backend* backend); /** * Similar to Session::Session, but also specify variable model for automatically handles model's updates */ explicit Session( Backend* backend, DefaultVariableModel* model); /** * Destructor */ ~Session() override; /** * Login to the Session. In this function you should do anything needed to set up * the session, and make it ready for usage. The method should be implemented non-blocking. * Emit loginStarted() prior to connection to the actual backend in order to notify cantor_part about it. * If the logging in is completed, the loginDone() signal must be emitted */ virtual void login() = 0; /** * Log out of the Session. Destroy everything specific to a single session, e.g. * stop all the running processes etc. Also logout session status must be Status::Disable * NOTE: restarting the session consists of first logout() and then login() */ virtual void logout() = 0; /** * Passes the given command to the backend and returns a Pointer * to a new Expression object, which will emit the gotResult() * signal as soon as the computation is done. The result will * then be accessible by Expression::result() * @param command the command that should be run by the backend. * @param finishingBehavior the FinishingBehaviour that should be used for this command. @see Expression::FinishingBehaviour * @param internal true, if it is an internal command @see Expression::Expression(Session*, bool) * @return an Expression object, representing this command */ virtual Expression* evaluateExpression(const QString& command, Expression::FinishingBehavior finishingBehavior = Expression::FinishingBehavior::DoNotDelete, bool internal = false) = 0; /** * Append the expression to queue . * @see expressionQueue() const */ void enqueueExpression(Expression*); /** * Interrupts all the running calculations in this session */ virtual void interrupt() = 0; /** * Returns tab-completion, for this command/command-part. * The return type is a CompletionObject. The fetching * of the completions works asynchronously, you'll have to * listen to the done() Signal of the returned object * @param cmd The partial command that should be completed * @param index The index (cursor position) at which completion * was invoked. Defaults to -1, indicating the end of the string. * @return a Completion object, representing this completion * @see CompletionObject */ virtual CompletionObject* completionFor(const QString& cmd, int index = -1); /** * Returns Syntax help, for this command. * It returns a SyntaxHelpObject, that will fetch the * needed information asynchronously. You need to listen * to the done() Signal of the Object * @param cmd the command, syntax help is requested for * @return SyntaxHelpObject, representing the help request * @see SyntaxHelpObject */ virtual SyntaxHelpObject* syntaxHelpFor(const QString& cmd); /** * returns a syntax highlighter for this session * @param parent QObject the Highlighter's parent * @return QSyntaxHighlighter doing the highlighting for this Session */ virtual QSyntaxHighlighter* syntaxHighlighter(QObject* parent); /** * returns a Model to interact with the variables or nullptr, if * this backend have a variable model, which not inherit from * default variable model class (in this case @see variableDataModel()) * @return DefaultVariableModel to interact with the variables */ virtual DefaultVariableModel* variableModel() const; /** * returns QAbstractItemModel to interact with the variables */ virtual QAbstractItemModel* variableDataModel() const; /** * Enables/disables Typesetting for this session. * For this setting to make effect, the Backend must support * LaTeX typesetting (as indicated by the capabilities() flag. * @param enable true to enable, false to disable typesetting */ virtual void setTypesettingEnabled(bool enable); /** * Updates the worksheet path in the session. * This can be useful to set the path of the currently opened * Cantor project file in the backend interpreter. * Default implementation does nothing. Derived classes have * to implement the proper logic if this feature is supported. * @param path the new absolute path to the worksheet. */ virtual void setWorksheetPath(const QString& path); /** * Returns the Backend, this Session is for * @return the Backend, this Session is for */ Backend* backend(); /** * Returns the status this Session has * @return the status this Session has */ Cantor::Session::Status status(); /** * Returns whether typesetting is enabled or not * @return whether typesetting is enabled or not */ bool isTypesettingEnabled(); /** * Returns the next available Expression id * It is basically a counter, incremented for * each new Expression * @return next Expression id */ int nextExpressionId(); protected: /** * Change the status of the Session. This will cause the * stausChanged signal to be emitted * @param newStatus the new status of the session */ void changeStatus(Cantor::Session::Status newStatus); /** * Session can process one single expression at one time. * Any other expressions submitted by the user are queued first until they get processed. * The expression queue implements the FIFO mechanism. * The queud expression have the status \c Expression::Queued. */ QList& expressionQueue() const; /** * Execute first expression in expression queue. * Also, this function changes the status from Queued to Computing. * @see expressionQueue() const */ virtual void runFirstExpression(); /** * This method dequeues the expression and goes to the next expression, if the queue is not empty. * Also, this method updates the variable model, if needed. * If the queue is empty, the session status is set to Done. * @param setDoneAfterUpdate enable setting status to Done after variable update, if queue is empty */ virtual void finishFirstExpression(bool setDoneAfterUpdate = false); /** * Starts variable update immideatly, usefull for subclasses, which run internal command * which could change variables listen */ virtual void updateVariables(); /** * Setting variable model, usefull, if model constructor requires functional session */ void setVariableModel(DefaultVariableModel* model); /** * Search file for session in AppDataLocation and in GenericDataLocation */ QString locateCantorFile(const QString& partialPath, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); + QStringList locateAllCantorFiles(const QString& partialPath, QStandardPaths::LocateOptions options = QStandardPaths::LocateFile); Q_SIGNALS: void statusChanged(Cantor::Session::Status newStatus); void loginStarted(); void loginDone(); void error(const QString& msg); private: SessionPrivate* d; }; } #endif /* _SESSION_H */