From 8b07718cb47dca6240a70e9aea57b88121cc956b Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Wed, 23 Dec 2015 23:25:09 +0100 Subject: akonadi2_cli -> akonadish --- akonadish/repl/repl.cpp | 78 +++++++++++++++++++ akonadish/repl/repl.h | 34 +++++++++ akonadish/repl/replStates.cpp | 171 ++++++++++++++++++++++++++++++++++++++++++ akonadish/repl/replStates.h | 87 +++++++++++++++++++++ 4 files changed, 370 insertions(+) create mode 100644 akonadish/repl/repl.cpp create mode 100644 akonadish/repl/repl.h create mode 100644 akonadish/repl/replStates.cpp create mode 100644 akonadish/repl/replStates.h (limited to 'akonadish/repl') diff --git a/akonadish/repl/repl.cpp b/akonadish/repl/repl.cpp new file mode 100644 index 0000000..395661e --- /dev/null +++ b/akonadish/repl/repl.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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 "repl.h" + +#include + +#include +#include +#include +#include + +#include "replStates.h" + +Repl::Repl(QObject *parent) + : QStateMachine(parent) +{ + // readline history setup + using_history(); + read_history(commandHistoryPath().toLocal8Bit()); + + // create all states + ReadState *read = new ReadState(this); + UnfinishedReadState *unfinishedRead = new UnfinishedReadState(this); + EvalState *eval = new EvalState(this); + PrintState *print = new PrintState(this); + QFinalState *final = new QFinalState(this); + + // connect the transitions + read->addTransition(read, SIGNAL(command(QString)), eval); + read->addTransition(read, SIGNAL(exitRequested()), final); + + unfinishedRead->addTransition(unfinishedRead, SIGNAL(command(QString)), eval); + unfinishedRead->addTransition(unfinishedRead, SIGNAL(exitRequested()), final); + + eval->addTransition(eval, SIGNAL(completed()), read); + eval->addTransition(eval, SIGNAL(continueInput()), unfinishedRead); + eval->addTransition(eval, SIGNAL(output(QString)), print); + + print->addTransition(print, SIGNAL(completed()), eval); + + setInitialState(read); + start(); +} + +Repl::~Repl() +{ + // readline history writing + write_history(commandHistoryPath().toLocal8Bit()); +} + +QString Repl::commandHistoryPath() +{ + const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + if (!QFile::exists(path)) { + QDir dir; + dir.mkpath(path); + } + return path + "/repl_history"; +} + +#include "moc_repl.cpp" diff --git a/akonadish/repl/repl.h b/akonadish/repl/repl.h new file mode 100644 index 0000000..b76c66b --- /dev/null +++ b/akonadish/repl/repl.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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. + */ + +#pragma once + +#include + +class Repl : public QStateMachine +{ + Q_OBJECT + +public: + Repl(QObject *parent = 0); + ~Repl(); + +private: + static QString commandHistoryPath(); +}; diff --git a/akonadish/repl/replStates.cpp b/akonadish/repl/replStates.cpp new file mode 100644 index 0000000..62888d0 --- /dev/null +++ b/akonadish/repl/replStates.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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 "replStates.h" + +#include +#include + +#include +#include + +#include +#include +#include + +#include "syntaxtree.h" + +static char *akonadi2_cli_next_tab_complete_match(const char *text, int state); +static char ** akonadi2_cli_tab_completion(const char *text, int start, int end); + +ReadState::ReadState(QState *parent) + : QState(parent) +{ + rl_completion_entry_function = akonadi2_cli_next_tab_complete_match; + rl_attempted_completion_function = akonadi2_cli_tab_completion; +} + +void ReadState::onEntry(QEvent *event) +{ + Q_UNUSED(event) + char *line = readline(prompt()); + + if (!line) { + std::cout << std::endl; + emit exitRequested(); + return; + } + + // we have actual data, so let's wait for a full line of text + QByteArray input(line); + const QString text = QString(line).simplified(); + //qDebug() << "Line is ... " << text; + + if (text.length() > 0) { + add_history(line); + } + + free(line); + emit command(text); +} + +const char *ReadState::prompt() const +{ + return "> "; +} + +UnfinishedReadState::UnfinishedReadState(QState *parent) + : ReadState(parent) +{ +} + +const char *UnfinishedReadState::prompt() const +{ + return " "; +} + +EvalState::EvalState(QState *parent) + : QState(parent) +{ +} + +void EvalState::onEntry(QEvent *event) +{ + QStateMachine::SignalEvent *e = dynamic_cast(event); + + const QString command = e ? e->arguments()[0].toString() : QString(); + + if (command.isEmpty()) { + complete(); + return; + } + + if (command.right(1) == "\\") { + m_partial += " " + command.left(command.size() - 1); + continueInput(); + } else { + m_partial += " " + command; + complete(); + } +} + +void EvalState::complete() +{ + m_partial = m_partial.simplified(); + + if (!m_partial.isEmpty()) { + //emit output("Processing ... " + command); + const QStringList commands = SyntaxTree::tokenize(m_partial); + SyntaxTree::self()->run(commands); + m_partial.clear(); + } + + emit completed(); +} + +PrintState::PrintState(QState *parent) + : QState(parent) +{ +} + +void PrintState::onEntry(QEvent *event) +{ + QStateMachine::SignalEvent *e = dynamic_cast(event); + + if (e && !e->arguments().isEmpty()) { + const QString command = e->arguments()[0].toString(); + QTextStream stream(stdout); + stream << command << "\n"; + } + + emit completed(); +} + +static QStringList tab_completion_full_state; +static bool tab_completion_at_root = false; + +static char **akonadi2_cli_tab_completion(const char *text, int start, int end) +{ + tab_completion_at_root = start == 0; + tab_completion_full_state = QString(rl_line_buffer).remove(start, end - start).split(" ", QString::SkipEmptyParts); + return NULL; +} + +static char *akonadi2_cli_next_tab_complete_match(const char *text, int state) +{ + const QString fragment(text); + Syntax::List nearest = SyntaxTree::self()->nearestSyntax(tab_completion_full_state, fragment); + //for (auto syntax: nearest) { qDebug() << "Nearest: " << syntax.keyword; } + + if (nearest.isEmpty()) { + SyntaxTree::Command command = SyntaxTree::self()->match(tab_completion_full_state); + if (command.first && command.first->completer) { + QStringList commandCompletions = command.first->completer(tab_completion_full_state, fragment); + if (commandCompletions.size() > state) { + return qstrdup(commandCompletions[state].toUtf8()); + } + } + } else if (nearest.size() > state) { + return qstrdup(nearest[state].keyword.toUtf8()); + } + + return rl_filename_completion_function(text, state); +} + +#include "moc_replStates.cpp" diff --git a/akonadish/repl/replStates.h b/akonadish/repl/replStates.h new file mode 100644 index 0000000..a0d3f90 --- /dev/null +++ b/akonadish/repl/replStates.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 Aaron Seigo + * + * 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. + */ + +#pragma once + +#include + +class QSocketNotifier; + +class ReadState : public QState +{ + Q_OBJECT + +public: + ReadState(QState *parent = 0); + +Q_SIGNALS: + void command(const QString &command); + void exitRequested(); + +protected: + void onEntry(QEvent *event); + virtual const char *prompt() const; +}; + +class UnfinishedReadState : public ReadState +{ + Q_OBJECT + +public: + UnfinishedReadState(QState *parent = 0); + +protected: + const char *prompt() const; +}; + +class EvalState : public QState +{ + Q_OBJECT + +public: + EvalState(QState *parent = 0); + +Q_SIGNALS: + void completed(); + void continueInput(); + void output(const QString &output); + +protected: + void onEntry(QEvent *event); + +private: + void complete(); + + QString m_partial; +}; + +class PrintState : public QState +{ + Q_OBJECT + +public: + PrintState(QState *parent = 0); + +Q_SIGNALS: + void completed(); + +protected: + void onEntry(QEvent *event); +}; + -- cgit v1.2.3 From 26db899f5a12d5ba960e778ce904e279e97481d8 Mon Sep 17 00:00:00 2001 From: Aaron Seigo Date: Fri, 25 Dec 2015 21:15:47 +0100 Subject: a little welcome banner --- akonadish/repl/repl.cpp | 13 +++++++++++++ akonadish/repl/repl.h | 1 + 2 files changed, 14 insertions(+) (limited to 'akonadish/repl') diff --git a/akonadish/repl/repl.cpp b/akonadish/repl/repl.cpp index 395661e..499a4af 100644 --- a/akonadish/repl/repl.cpp +++ b/akonadish/repl/repl.cpp @@ -25,8 +25,10 @@ #include #include #include +#include #include "replStates.h" +#include "syntaxtree.h" Repl::Repl(QObject *parent) : QStateMachine(parent) @@ -56,6 +58,7 @@ Repl::Repl(QObject *parent) print->addTransition(print, SIGNAL(completed()), eval); setInitialState(read); + printWelcomeBanner(); start(); } @@ -65,13 +68,23 @@ Repl::~Repl() write_history(commandHistoryPath().toLocal8Bit()); } +void Repl::printWelcomeBanner() +{ + QTextStream out(stdout); + out << QObject::tr("Welcome to the Akonadi2 interative shell!\n"); + out << QObject::tr("Type `help` for information on the available commands.\n"); + out.flush(); +} + QString Repl::commandHistoryPath() { const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + if (!QFile::exists(path)) { QDir dir; dir.mkpath(path); } + return path + "/repl_history"; } diff --git a/akonadish/repl/repl.h b/akonadish/repl/repl.h index b76c66b..d8d2533 100644 --- a/akonadish/repl/repl.h +++ b/akonadish/repl/repl.h @@ -30,5 +30,6 @@ public: ~Repl(); private: + static void printWelcomeBanner(); static QString commandHistoryPath(); }; -- cgit v1.2.3